gulp 任务怎么写
const gulp = require('gulp');
gulp.task('default', function() {
gulp.src('js/*.js')
.pipe(uglify())
.pipe(gulp.dest('dist'));
});
注意:默认的,task 将以最大的并发数执行,gulp 会一次性运行所有的 task 并且不做任何等待。如果你想要创建一个序列化的 task 队列,并以特定的顺序执行,你需要做两件事:
- 给出一个提示,来告知 task 什么时候执行完毕
- 并且再给出一个提示,来告知一个 task 依赖另一个 task 的完成
var gulp = require('gulp');
// 返回一个 callback,因此系统可以知道它什么时候完成
gulp.task('one', function(cb) {
// 做一些事 -- 异步的或者其他的
cb(err); // 如果 err 不是 null 或 undefined,则会停止执行,且注意,这样代表执行失败了
这里也可以使用 return 代替 cb()
});
// 定义一个所依赖的 task 必须在这个 task 执行之前完成
gulp.task('two', ['one'], function() {
// 'one' 完成后
});
gulp.task('default', ['one', 'two']);
gulp 核心知识
个人认为了解 gulp 的核心知识,主要从三个方面来了解:
- 流
- 文件系统
- 任务管理
上面三点分别对应了gulp的 pipe(这样写不太严谨,因为pipe是流的api),src和task 这三个API,了解了这三部分,gulp的使用,插件的编写都会比较得心应手。
流(Stream)
说实话到现在也没有对流的所有知识十分了解,之前查阅了一些 node 相关的书,感觉很多书写的东西和例子都很浅,后来看了一些文章,算是了解了一点皮毛。这是网传的学习流必看的文章,有兴趣的可以看看。
本文默认读者对流有了一定的了解,因此这里对流的种类,相关API和使用不会说的很详细。
流的好处
stream不会占用大量内存。例如 fs.readFile 在读取文件时,会把整个文件读取到内存当中,当文件很大时,会很占用内存,甚至会超出v8的内存限制,导致程序退出。流则是读一部分,写一部分,而且能够充分地利用 Buffer 不受 V8 内存控制的特点,利用堆外内存完成高效地传输。
gulp 中的流
gulp 内部使用了 through2 封装好的 transform 流(后面会展开说),这一步是在 gulp.src 的时候完成的,因此我们可以看到这样的代码:
gulp.src(xxx).pipe(xxx)...
我个人理解的 pipe 就是一根水管,左边是可读流(readStream),右边是可写流(writeStream)。正因为 gulp 帮我们封装好了流,因此,我们可以直接使用 pipe 这个 api 来传输数据。
gulp 插件的编写
学习插件的编写能让我们对gulp中流的使用有更深入的认识,有的时候可能我们需要一些定制话的功能来处理文件,这时就需要自己编写gulp的插件了,我们先来看一个官方的例子:
点击打开
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;
// 常量
const PLUGIN_NAME = 'gulp-prefixer';
function prefixStream(prefixText) {
var stream = through();
stream.write(prefixText);
return stream;
}
// 插件级别函数 (处理文件)
function gulpPrefixer(prefixText) {
if (!prefixText) {
throw new PluginError(PLUGIN_NAME, 'Missing prefix text!');
}
prefixText = new Buffer(prefixText); // 预先分配
// 创建一个让每个文件通过的 stream 通道
return through.obj(function(file, enc, cb) {
if (file.isNull()) {
// 返回空文件
cb(null, file);
}
if (file.isBuffer()) {
file.contents = Buffer.concat([prefixText, file.contents]);
}
if (file.isStream()) {
file.contents = file.contents.pipe(prefixStream(prefixText));
}
cb(null, file);
});
};
// 暴露(export)插件主函数
module.exports = gulpPrefixer;
对于 gulp 插件的编写我认为这两点是比较重要的:
- through2 做了什么
- through2.obj 中的 function 怎么写
带着这些疑问,我们先看下 through2 的源码:源码地址
点击打开
// 前面的代码创建了一个 Transform 流,并给这个流添加了一个 destroy 方法
function through2 (construct) {
return function (options, transform, flush) {
if (typeof options == 'function') {
flush = transform
transform = options
options = {}
}
if (typeof transform != 'function')
transform = noop
if (typeof flush != 'function')
flush = null
return construct(options, transform, flush)
}
}
module.exports.obj = through2(function (options, transform, flush) {
var t2 = new DestroyableTransform(Object.assign({ objectMode: true, highWaterMark: 16 }, options))
t2._transform = transform
if (flush)
t2._flush = flush
return t2
})
- 编写插件时要使用 through2.obj 方法
- _transform 方法最后要执行一下 callback 后才能接收下一个数据块
文件系统
文件系统这个名字说着有点悬乎,其实这部分就是介绍一下 gulp.src 是怎么把文件加工成流的。
未完待续。。。