共计 6948 个字符,预计需要花费 18 分钟才能阅读完成。
安装
Gulp 官网
gulp4.0 分离了处理和核心部分,所以需要分别安装这两个包,另外对环境要求如下:
node >= 8.11.1
npm >= 5.6.0
npx >= 9.7.1
- 全局安装 gulp-cli
npm i -g gulp-cli
- 本地安装 gulp
npm i -D gulp
- 查看版本号
$ gulp -v
# 输出
CLI version: 2.2.0
Local version: 4.0.2
配置文件
在项目根目录创建 gulpfile.js
文件(如果使用 ts 或者 babel,也可用 gulpfile.ts
、gulpfile.babel.js
分别代替),此文件即 gulp 会默认读取的配置文件,我们可以在里面配置需要的 task。
如果 task 较多或者较复杂,可以创建 gulpfile.js
目录,在目录中拆分 task 为多个文件,只要保证该目录下有个 index.js
作为入口即可。
Task
task 分为两种:
- Private tasks:配置文件中的一个 function,仅能在该文件中使用
- Public tasks:将 Private tasks 导出,可以供 gulp 命令执行
const {series, parallel} = require('gulp');
// Private tasks
function clean(cb) {
// body omitted
cb();}
// Private tasks
function build(cb) {
// body omitted
cb();}
exports.build = build; // Public tasks, 执行 gulp build
exports.default = series(clean, parallel(css, javascript)); // Public tasks, 执行 gulp
注意:
在 task 中,操作完成时,我们必须要通过 cb()或者 return 的方式来告知 gulp 此任务已完成。
// cb
function clean(cb) {del(['dist]);
cb();});
// return
function minifyjs() {return src('src/**/*.js')
.pipe(minify())
.pipe(dest('dist'));
});
function promiseTask() {return new Promise(function(resolve, reject) {
// body omitted
resolve();});
});
运行 task
gulp <export task name>
gulp // 导出为 default 的 task 可以直接允许 gulp
组合 task
- series:序列(顺序执行)
// task1 执行完再执行 task2
exports.taskName = series(task1, task2)
- parallel:并行(同时执行)
// task1 和 task2 同时执行
exports.taskName = parallel(task1, task2)
- 混用:
exports.taskName = series(clean, parallel(css, javascript))
输入与输出
gulp 借鉴了 Unix 的管道(pipe)思想,处理文件采用流的方式,前一步的输出作为后一步的输入,中途不会在磁盘写入文件,仅在 dest 时输出文件,所以非常快速高效。
gulp 提供了 src 及 dest 方法分别来进行文件读入、输出操作,同时提供了 pipe 管道方法来链式执行其他操作。
const {src, dest} = require('gulp');
// 将 src 目录下的所有 js 输出到 output 目录
exports.default = function() {return src('src/*.js')
.pipe(dest('output/'));
}
如果我们想在中途添加文件可以采用如下方式:
const {src, dest} = require('gulp');
const uglify = require('gulp-uglify');
exports.default = function() {return src('src/*.js')
.pipe(uglify())
.pipe(src('vendor/*.js')) // 添加文件
.pipe(dest('output/'));
}
当然我们也可以进行多次输出:
const {src, dest} = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
exports.default = function() {return src('src/*.js')
.pipe(babel())
.pipe(dest('temp/'))
.pipe(uglify())
.pipe(dest('output/'));
}
文件匹配
往往我们在使用 src 方法的时候需要输入多个或者一类文件,而不仅仅是某个具体的文件,这时我们就可以使用 gulp 提供的匹配规则来处理。
-
"src/file.js"
:单个文件 -
["src/file1,src/file2.js"]
:多个文件 -
*
: 所有文件
src('src/*.js') // src 自身目录所有的 js 文件,不含后代文件夹中
src('src/a*c.js')
-
**
:0 或者多个文件夹
src('src/**/*.js') // src 目录所有的 js 文件,含后代文件夹中的
-
{}
:多个属性
src('src/*.{jpg,png,gif}') // src 自身目录下的所有 jpg、png 和 gif 文件
-
!
:排除
src(['**/*.js', '!node_modules/**']) // 所有的 js 文件,但是 node_modules 下的除外
注意:src 接收的文件匹配字符串会顺序解释,所以你可以写成这样 gulp.src(['.js', '!b.js', 'bad.js'])
(排除所有以 b 开头的 JS 文件但是除了 bad.js
)
使用插件和其他库
gulp 推荐每个插件应该只专注的做一小部分工作,然后通过 pipe 将它们连接起来,就可以完成我们需要做的事情。
const {src, dest} = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
exports.default = function() {return src('src/*.js')
// The gulp-uglify plugin won't update the filename
.pipe(uglify())
// So use gulp-rename to change the extension
.pipe(rename({ extname: '.min.js'}))
.pipe(dest('output/'));
}
当然,除了插件,我们也可以使用其他库:
const del = require('delete');
exports.default = function(cb) {
// Use the `delete` module directly, instead of using gulp-rimraf
del(['output/*.js'], cb);
}
最后也可以借助 through2 插件使用内联方式自行处理:
const {src, dest} = require('gulp');
const uglify = require('uglify-js');
const through2 = require('through2');
exports.default = function() {return src('src/*.js')
// Instead of using gulp-uglify, you can create an inline plugin
.pipe(through2.obj(function(file, _, cb) {if (file.isBuffer()) {const code = uglify.minify(file.contents.toString())
file.contents = Buffer.from(code)
}
cb(null, file);
}))
.pipe(dest('output/'));
}
监听文件
我们可以使用 watch 方法来监听文件的改动,以便在改动时执行相应的处理任务。
const {watch, series} = require('gulp');
function clean(cb) {
// body omitted
cb();}
function javascript(cb) {
// body omitted
cb();}
function css(cb) {
// body omitted
cb();}
exports.default = function() {watch('src/*.css', css);
watch('src/*.js', series(clean, javascript));
};
API
src(globs, [options])
:输入
-
globs[string|array]
: 要处理文件的路径匹配规则 -
options
:配置项,点击查看详情
const {src, dest} = require('gulp');
function copy() {return src('input/*.js', { sourcemaps: true})
.pipe(dest('output/'));
}
dest(directory, [options])
:输出
-
directory[string|function]
: 输出的路径 -
options
:配置项,点击查看详情
const {src, dest} = require('gulp');
const uglify = require('gulp-uglify');
src('input/**/*.js', { sourcemaps: true})
.pipe(uglify())
.pipe(dest('output/', { sourcemaps: '.'}));
series(...tasks)
:顺序执行多个任务
-
tasks[function|string]
:任务名或者 function
const {series} = require('gulp');
function javascript(cb) {
// body omitted
cb();}
function css(cb) {
// body omitted
cb();}
exports.build = series(javascript, css);
parallel(...tasks)
:多个任务同时执行
-
tasks[function|string]
:任务名或者 function
const {parallel} = require('gulp');
function javascript(cb) {
// body omitted
cb();}
function css(cb) {
// body omitted
cb();}
exports.build = parallel(javascript, css);
watch(globs, [options], [task])
:文件监听
-
globs[string|array]
: 要监听的文件 -
options
:配置项,点击查看详情 -
task[function|string]
:要执行的任务或操作
const {watch} = require('gulp');
watch(['input/*.js', '!input/something.js'], function(cb) {
// body omitted
cb();});
监听方法会返回一共实例,该实例提供了如下几个方法:
watcher.on(eventName, eventHandler)
-
eventName[string]
:事件名称,可以是add
,addDir
,change
,unlink
,unlinkDir
,ready
,error
, orall
-
eventHandler[function]
:事件处理函数,该函数接收 path 和 stats 两个参数。
const {watch} = require('gulp');
const watcher = watch(['input/*.js']);
watcher.on('change', function(path, stats) {console.log(`File ${path} was changed`);
});
watcher.on('add', function(path, stats) {console.log(`File ${path} was added`);
});
watcher.on('unlink', function(path, stats) {console.log(`File ${path} was removed`);
});
watcher.close();
watcher.close()
:关闭文件监听器
watcher.add(globs)
:添加文件到监听器
-
globs[string|array]
: 要添加的文件
watcher.unwatch(globs)
:移除监听器中的文件
-
globs[string|array]
: 要移除的文件
task([taskName], taskFunction)
:定义任务(4.0 推荐使用 function 替代此方法)
-
taskName[string]
:任务名称 -
taskFunction[function]
:处理函数
const {task} = require('gulp');
task('build', function(cb) {
// body omitted
cb();});
const build = task('build');
lastRun(task, [precision])
:获取任务最后运行完成的时间戳
-
task[function|string]
:指定获取的任务 -
precision[number]
:精度,默认 1000
可使用此方法进行增量编译。
const {src, dest, lastRun, watch} = require('gulp');
const imagemin = require('gulp-imagemin');
function images() {return src('src/images/**/*.jpg', { since: lastRun(images) })
.pipe(imagemin())
.pipe(dest('build/img/'));
}
exports.default = function() {watch('src/images/**/*.jpg', images);
};
tree([options])
:获取任务依赖关系
-
options[object]
:deep 默认为 false,只会返回顶级任务,如果为 true,则会返回整个任务树。
gulp.tree({deep: true})
我常用的 api 就这些,其他的 api 可以自行查看官方文档。
常用插件
- gulp-clean:用于清理;
- gulp-notify:用于打印消息文本;
- gulp-rename:用于修改名字;
- gulp-concat:用于合并文件;
- gulp-zip:用于生成一个 zip 压缩包;
- gulp-minify-css:用于压缩 css;
- gulp-autoprefixer:用于给 css 添加前缀;
- gulp-imagemin:用于给图片进行优化;
- gulp-uglify:用于压缩 js;
- amd-optimize:用于 amd 模块引用编译;
- gulp-import-css:如果 css 文件是通过 import 导入的可以使用此插件进行合并优化;
- gulp-rev-replace:用于替换;
- gulp-useref:引入使用 build 标记,进行替换;
- gulp-rev:生成 md5 文件名;
- gulp-filter:对文件进行过滤;
- gulp-header:压缩之后将注释写入到文件的头部
- gulp-if:进行逻辑判断
- gulp-size:获取文件大小
- gulp-less:编译 less 文件
- gulp-sass:编译 sass 文件
- gulp-file-include:对文件进行引入
- gulp-sourcemaps:生成 map 文件
- gulp-livereload:自动刷新
- gulp-clean-css:css 压缩
- browserSync:启动 server 并启动热更新
- gulp-plumber : 监测工作流,报错,防止遇到错误时直接退出 gulp
- gulp-rev : 文件名添加版本号
- gulp-css-spritesmith:根据 css 文件自动生成雪碧图
如果要查找 gulp 插件,一般有两个地方:
- 官方插件库
- npm 仓库
总结:
gulp 本身其实非常简单,提供的 api 很少,但是简洁够用。在了解这些 api 后,你可能觉得最复杂的还是了解各插件的用法。
其实,构建工具(gulp、webpack 之类)本身都是相对较简单的,这才是它们该有的样子,本身就很复杂了,我才懒得用。但是在使用过程中,我觉得有两个难点:
- 如何制定一套合理、可配置、能够真正提升我们工作效率的构建流程
- 如何在需要某个功能时,能够快速找到最合适的那个插件,且能够快速使用
最后,本人才疏学浅,有不妥之处,欢迎指正。