gulp.src() 内部实现探究
首先我们看下 gulpfile
里面的内容是长什么样子的,很有 express 中间件的味道是不是,我们知道 .pipe()
是典型的流式操作的API。很自然的,我们会想到 gulp.src()
这个API返回的应该是个 Stream 对象(也许经过层层封装)。本着一探究竟的目的,花了点时间把 gulp 的源码大致扫了下,终于找到了答案。
gulpfile.js
var gulp = require('gulp'),
preprocess = require('gulp-preprocess');
gulp.task('default', function() {
gulp.src('src/index.html')
.pipe(preprocess({USERNAME:'程序猿小卡'}))
.pipe(gulp.dest('dest/'));
});
提前剧透
此处有内容剧透,如有对剧透不适者,请自行跳过本段落。gulp.src() 的确返回了定制化的 Stream 对象。可以在 github 上搜索 ordered-read-streams
这个项目。
大致关系是:
ordered-read-streams --> glob-stream --> vinyl-fs --> gulp.src()
探究之路
首先,我们看下require('gulp')
返回了什么。从gulp的源码来看,返回了 Gulp
对象,该对象上有src
、pipe
、dest
等方法。很好,找到了我们想要的src
方法。接着往下看
参考:https://github.com/gulpjs/gulp/blob/master/index.js#L62
gulp/index.js
var inst = new Gulp();
module.exports = inst;
从下面的代码可以看到,gulp.src
方法,实际上是vfs.src
。继续
参考:https://github.com/gulpjs/gulp/blob/master/index.js#L25
gulp/index.js
var vfs = require('vinyl-fs');
// 省略很多行代码
Gulp.prototype.src = vfs.src;
接下来我们看下vfs.src
这个方法。从vinyl-fs/index.js
可以看到,vfs.src
实际是vinyl-fs/lib/src/index.js
。
参考:https://github.com/wearefractal/vinyl-fs/blob/master/index.js
vinyl-fs/index.js
'use strict';
module.exports = {
src: require('./lib/src'),
dest: require('./lib/dest'),
watch: require('glob-watcher')
};
那么,我们看下vinyl-fs/lib/src/index.js
。可以看到,gulp.src()
返回的,实际是outputStream
这货,而outputStream
是gs.create(glob, options).pipe()
获得的,差不多接近真相了,还有几步而已。
参考:https://github.com/wearefractal/vinyl-fs/blob/master/lib/src/index.js#L37
vinyl-fs/lib/src/index.js
var defaults = require('lodash.defaults');
var through = require('through2');
var gs = require('glob-stream');
var File = require('vinyl');
// 省略非重要代码若干行
function src(glob, opt) {
// 继续省略代码
var globStream = gs.create(glob, options);
// when people write to use just pass it through
var outputStream = globStream
.pipe(through.obj(createFile))
.pipe(getStats(options));
if (options.read !== false) {
outputStream = outputStream
.pipe(getContents(options));
}
// 就是这里了
return outputStream
.pipe(through.obj());
}
我们再看看glob-stream/index.js
里的create
方法,最后的return aggregate.pipe(uniqueStream);
。好的,下一步就是真相了,我们去ordered-read-streams
这个项目一探究竟。
参考:https://github.com/wearefractal/glob-stream/blob/master/index.js#L89
glob-stream/index.js
var through2 = require('through2');
var Combine = require('ordered-read-streams');
var unique = require('unique-stream');
var glob = require('glob');
var minimatch = require('minimatch');
var glob2base = require('glob2base');
var path = require('path');
// 必须省略很多代码
// create 方法
create: function(globs, opt) {
// 继续省略代码
// create all individual streams
var streams = positives.map(function(glob){
return gs.createStream(glob, negatives, opt);
});
// then just pipe them to a single unique stream and return it
var aggregate = new Combine(streams);
var uniqueStream = unique('path');
// TODO: set up streaming queue so items come in order
return aggregate.pipe(uniqueStream);
真相来了,我们看下ordered-read-streams
的代码,可能刚开始看不是很懂,没关系,知道它实现了自己的Stream
就可以了(nodejs是有暴露相应的API让开发者对Stream进行定制的),具体可参考:http://www.nodejs.org/api/stream.html#stream_api_for_stream_implementors
代码来自:https://github.com/armed/ordered-read-streams/blob/master/index.js
ordered-read-streams/index.js
function OrderedStreams(streams, options) {
if (!(this instanceof(OrderedStreams))) {
return new OrderedStreams(streams, options);
}
streams = streams || [];
options = options || {};
if (!Array.isArray(streams)) {
streams = [streams];
}
options.objectMode = true;
Readable.call(this, options);
// stream data buffer
this._buffs = [];
if (streams.length === 0) {
this.push(null); // no streams, close
return;
}
streams.forEach(function (s, i) {
if (!s.readable) {
throw new Error('All input streams must be readable');
}
s.on('error', function (e) {
this.emit('error', e);
}.bind(this));
var buff = [];
this._buffs.push(buff);
s.on('data', buff.unshift.bind(buff));
s.on('end', flushStreamAtIndex.bind(this, i));
}, this);
}
参考:https://github.com/armed/ordered-read-streams/blob/master/index.js
写在后面
兜兜转转一大圈,终于找到了 gulp.src()
的源头,大致流程如下,算是蛮深的层级。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论