gulp.src()内部实现探究

简介:

写在前面

本来是想写个如何编写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对象,该对象上有srcpipedest等方法。很好,找到了我们想要的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这货,而outputStreamgs.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()


相关文章
|
编解码 虚拟化 Windows
通过Hyper分辨率适配技术,可以解决虚拟机显示器上的分辨率适配难题
Hyper-V的增强会话模式通过RDP协议实现虚拟机与物理显示器的高质量连接,支持更高分辨率、优化图形性能,并提供共享剪贴板、拖放文件等增强功能。启用此模式可解决虚拟机分辨率适配难题,提升显示效果和交互性。需确保操作系统支持RDP,网络稳定,且注意对主机性能的影响。
|
开发者
Qt中的事件该如何学习?(附带案例)
事件是Qt中比较重要的一部分,在初期如果理解不当学习可能会比较困难,这里提一嘴当初教我的那位老师水平是真的高,让我很轻易的就理解了事件的概念。 在平时我们见到那些界面上的某些快捷键就有可能是事件做的,例如ESC关闭窗口,Enter提交或者登录这种类似的,这也是事件的强大之处。
736 1
|
数据可视化 API 数据处理
【Python篇】matplotlib超详细教程-由入门到精通(上篇)
【Python篇】matplotlib超详细教程-由入门到精通(上篇)
1273 5
|
网络协议 Ubuntu Linux
用Qemu模拟vexpress-a9 (三)--- 实现用u-boot引导Linux内核
用Qemu模拟vexpress-a9 (三)--- 实现用u-boot引导Linux内核
1076 1
|
JavaScript 前端开发 开发者
Vue学习之--------深入理解Vuex、原理详解、实战应用(2022/9/1)
这篇文章详细介绍了Vuex的基本概念、使用场景、安装配置、基本用法、实际应用案例以及注意事项,通过一个数字累加器的实战示例,帮助开发者深入理解Vuex的原理和应用。
|
存储 编解码 Linux
rodert教你学FFmpeg实战这一篇就够了 - 音视频处理入门篇
rodert教你学FFmpeg实战这一篇就够了 - 音视频处理入门篇
396 1
|
数据采集 Dart Apache
Github 2024-05-07 Python开源项目日报 Top10
在2024年5月7日的Github Trendings中,Python开源项目占据主导,共有10个项目上榜。其中热门项目包括:yt-dlp,一个增强版的youtube-dl分支,具有64K+星标;Home Assistant,专注本地控制和隐私的开源家庭自动化项目,拥有65K+星标;以及openpilot,一个开源驾驶辅助系统,支持多种车型,45K+星标。其他项目涵盖爬虫工具、实时应用框架Flet、可观测性平台Logfire等,涉及Python、Dart和C++等多种语言。
1071 10
|
算法
计算空间物体包围球的两种算法实现
计算空间物体包围球的两种算法实现
237 0
|
Linux 网络安全
linux配置IP访问权限
linux配置IP访问权限
803 1

热门文章

最新文章