Gulp >> 是基于 流 的自动化构建系统,是前端开发过程中对代码进行构建的工具;gulp不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用gulp,我们不仅可以很愉快的编写代码,而且大大提高我们的工作效率。
gulp是基于 nodeJS 的自动任务运行器, 它能自动化地完成 javascript
/ sass
/ less
/ html
/ image
/ css
等文件的的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成等,并监听文件在改动后重复指定的这些步骤。在实现上,gulp借鉴了Unix操作系统的管道(pipe
)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。通过本章,我们将学习如何使用gulp来改变开发流程,从而使开发更加快速高效。
Gulp 特点:
- 任务化:所有的构建操作,在 gulp 中都称之为 任务
- 基于流:gulp 中所有的文件操作,都是基于 流 方式进行 ( Gulp有一个自己的内存,通过指定 API 将源文件流到内存中,完成相应的操作后再通过相应的 API 流出去)
首先看一篇文章 Gulp的目标是取代Grunt >>
根据 gulp 的文档,它努力实现的主要特性是:
- 易于使用:采用代码优于配置策略,gulp 让简单的事情继续简单,复杂的任务变得可管理。
- 高效:通过利用 node.js 强大的流,不需要往磁盘写中间文件,可以更快地完成构建。
- 高质量:gulp 严格的插件指导方针,确保插件简单并且按你期望的方式工作。
- 易于学习:通过把 API 降到最少,你能在很短的时间内学会 gulp。构建工作就像你设想的一样:是一系列流管道。
Gulp通过 流和代码优于配置 的策略来简化任务编写的工作。
使用gulp,仅需知道4个API即可:gulp.task()
, gulp.src()
, gulp.dest()
, gulp.watch()
,所以很容易就能掌握。
在开始介绍之前,建议大家先看看 Gulp 中的一些概念 >>
1. gulp.src()
读取数据源并创建一个流,用于从文件系统读取 Vinyl 对象。需要注意的是,这个流里的内容不是原始的文件流,而是一个虚拟文件对象流( Vinyl files ),这个虚拟文件对象中存储着原始文件的路径、文件名、内容等信息,这个我们暂时不用去深入理解,你只需简单的理解可以用这个方法来读取你需要操作的文件就行了。其语法为:
gulp.src(globs, [options])
参数解读
-
globs
:参数是文件匹配模式(类似正则表达式),用来匹配文件路径(包括文件名),当然这里也可以直接指定某个具体的文件路径。当有多个匹配模式时,该参数可以为一个数组。 -
options
:可选参数,通常情况下我们不需要用到。
gulp 内部使用了 node-glob 模块来实现其文件匹配功能。我们可以使用下面这些特殊的字符来匹配我们想要的文件:
# | 描述 |
---|---|
* |
匹配文件路径中的0个或多个字符,但不会匹配路径分隔符 / ,除非路径分隔符出现在末尾。 |
** |
匹配文件路径中的0个或多个目录及其子目录,需要单独出现,即它左右不能有其他东西了。如果出现在末尾,也能匹配文件。 |
? |
匹配文件路径中的单个字符(不会匹配路径分隔符) |
! |
取反 |
想了解更多 glob 匹配规则,请 参考这里 >>
2. gulp.dest()
输出数据流到目标路径,语法形式如下:
gulp.dest(path[, options])
参数解读
path
:为写入文件的路径options
:为一个可选的参数对象,通常我们不需要用到
3. gulp.task()
该方法用来定义任务,其语法为:
gulp.task([taskName], taskFunction)
提示:这个API不再是推荐的模式了,现在主要是直接在文件中直接导出任务函数,请 参考这里 >>
可以通过 series、parallel 和 lastRun api 访问该任务:
# 1. 顺序执行
gulp.series(...tasks)
# 2. 并行执行
gulp.parallel(...tasks)
# 3. 最后执行
gulp.lastRun(task, [precision])
4. gulp.watch()
监听 globs 并在发生更改时运行任务。任务与任务系统的其余部分被统一处理。
watch(globs, [options], [task])
参数解读
-
globs
:为要监视的文件匹配模式,规则和用法与gulp.src
() 方法中的glob
相同。 -
options
:为一个可选的配置对象,通常不需要用到。 -
task
:任务函数
gulp.watch("./src/js/*.js", gulp.parallel("handleJS"));
① 创建项目
$ mkdir hello-gulp && cd hello-gulp && npm init -y
② 安装依赖
$ npm install gulp --save-dev
查看版本:
$ ./node_modules/.bin/gulp --version
CLI version: 2.3.0
Local version: 4.0.2
③ 创建 gulpfile 文件
在根目录创建 gulpfile.js
文件,并创建相应任务:
const { parallel, series } = require('gulp');
// task a
const a = (done) => {
setTimeout(() => {
console.log('Task a is complete!');
done();
}, 1000);
};
// task b
const b = (done) => {
setTimeout(() => {
console.log('Task b is complete!');
done();
}, 1000);
};
// task c
const c = (done) => {
setTimeout(() => {
console.log('Task c is complete!');
done();
}, 1000);
};
// -- 默认任务
exports.default = series;
// -- 串行方式执行任务,先执行a, 然后执行b, 然后执行c
exports.series = series(a, b, c);
// -- 并行方式执行任务,同时执行a,b,c
exports.parallel = parallel(a, b, c);
提示:
series()
和parallel()
可以被嵌套到任意深度。通过这两个函数,构建任务可以被任意排列组合,从而满足各种复杂的构建需求。
④ 运行任务
接下来,我们执行 gulp 任务:
$ ./node_modules/.bin/gulp # 执行默认任务(default)
$ ./node_modules/.bin/gulp series # 执行series任务
$ ./node_modules/.bin/gulp parallel # 执行parallel任务
gulp 暴露了 src()
和 dest()
方法用于处理计算机上存放的文件。在代码构建过程中,需要将源文件,写入到目标目录。
const { src, dest } = require('gulp');
exports.default = () => {
// 将 src/app 目录下的 index.js 文件,复制到 dist/app 目录下
return src('src/app/index.js', { base: 'src' }).pipe(dest('dist'));
};
gulp 内部实现用了三个SDK实现:
undertaker
:主要用来实现gulp的子任务流程管理vinyl-fs
:.src
接口可以匹配一个文件 通配符,将匹配到的文件转为Vinyl Stream
(流),gulp
理念就是万物皆可流。glob-watcher
:监控文件流变化
核心就是把文件转换成 Stream
流,然后对 Stream
进行操作。
所以 gulp
采用 pipe
(管道)的概念,意味着顺着管道流淌,然后我们对于 gulp
的插件,也很好理解了,就是在管道中间有个过滤站,对流进行过滤处理。
- gulp-rev:给静态资源文件名添加hash值,并生成map文件,当文件内容有更改时,打包后的文件名的hash值会自动改变,同时map文件也会自动更新
- gulp-rev-collector:按照生成的map文件,在html文件中
引入对应的css/js文件
- del:删除文件
- browser-sync:开发服务器
- gulp-rename:文件更名
- gulp-less:编译less
- gulp-sass:编译sass
- gulp-postcss:处理CSS,内置插件可以压缩(cssnano),自动注入前缀(autoprefixer),删除多余代码(purgecss)
- gulp-babel:编译ECMAScript
- gulp-uglify:压缩JavaScript
- gulp-htmlmin:压缩HTML
- gulp-imagemin:压缩图片
- vinyl-paths:在管道中操作文件(比如在管道中删除)
- gulp-cache:缓存,可避免重复压缩图片
- gulp-load-plugins:插件加载
- gulp-if:流控制(相当于JavaScript中的if语句)
- gulp-concat:文件合并
- gulp-filter:文件过滤
安装依赖:
# 通用
$ npm install gulp gulp-rename gulp-load-plugins del browser-sync --save-dev
# 样式
$ npm install gulp-postcss cssnano autoprefixer @fullhuman/postcss-purgecss gulp-less --save-dev
# 脚本
$ npm install gulp-babel @babel/core @babel/cli @babel/preset-env gulp-uglify --save-dev
# 模板
$ npm install gulp-htmlmin --save-dev
# 图片
$ npm install [email protected] --save-dev
配置代码:
/*
* @Author: Lee
* @Date: 2021-12-26 01:56:07
* @LastEditors: Lee
* @LastEditTime: 2021-12-26 18:07:45
*/
// -- 导入模块
const { src, dest, parallel, series, watch } = require('gulp');
const browserSync = require('browser-sync');
const bs = browserSync.create();
const del = require('del');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const purgecss = require('@fullhuman/postcss-purgecss');
const $ = require('gulp-load-plugins')();
// -- 清除文件
const clean = () => {
return del(['dist']);
};
// -- 服务 & 热更新
const serve = () => {
// watch(被监视的文件,对应的任务)
watch('src/index.html', html);
watch('src/**/*.less', style);
watch('src/**/*.js', script);
watch('src/images/**', image);
// 初始化服务
bs.init({
notify: false, // 禁用浏览器右上角的 browserSync connected 提示框
files: 'dist/**', // 监视 dist 下 文件的变化,然后在浏览器上实时更新
server: {
baseDir: './dist', // 指定服务启动的目录
},
port: 5000,
});
};
// -- 处理样式
const style = () => {
var plugins = [
autoprefixer({ overrideBrowserslist: ['last 2 version'] }),
cssnano(),
purgecss({
content: ['./src/**/*.html'],
}),
];
return src('src/styles/*.less', { base: 'src' })
.pipe($.less())
.pipe($.postcss(plugins))
.pipe($.rename({ extname: '.min.css' }))
.pipe(dest('dist'));
};
// -- 处理脚本
const script = () => {
return src('src/**/*.js')
.pipe(
$.babel({
presets: ['@babel/preset-env'],
})
)
.pipe($.uglify())
.pipe($.rename({ extname: '.min.js' }))
.pipe(dest('dist'));
};
// -- 处理模板
const html = () => {
return src('src/**/*.html')
.pipe(
$.htmlmin({
collapseWhitespace: true, // 去除标签之间多余的空行和空白
minifyCSS: true, // 压缩HTML中的CSS代码
minifyJS: true, // 压缩HTML中的JS代码
})
)
.pipe(dest('dist'));
};
// -- 处理图片
const image = () => {
return src('src/images/**', { base: 'src' })
.pipe($.imagemin())
.pipe(dest('dist'));
};
const build = series(clean, parallel([style, script, html, image]));
const dev = series(build, serve);
module.exports = {
build,
dev,
serve,
};
处理缓存,加 hash 值,请 参考这里 >>