寫在前面
本來是想寫個如何編寫gulp插件的科普文的,突然探究欲又發作了,于是就有了這篇東西。。。翻了下源碼看了下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()
的源頭,大致流程如下,算是蠻深的層級。代碼細節神馬的,有興趣的同學可以深究一下。
ordered-read-streams --> glob-stream --> vinyl-fs --> gulp.src()
文章列表