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对象,该对象上有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()
gulp.src()内部实现探究的更多相关文章
- 温故而知新 gulp.src 指定数组文件夹
gulp.src语法是基于这个库来实现的,所以详情请看这个API: https://www.gulpjs.com.cn/docs/api/ https://github.com/isaacs/node ...
- Gulp:插件编写入门
之前挖了个坑,准备写篇gulp插件编写入门的科普文,之后迟迟没有动笔,因为不知道该肿么讲清楚Stream这货,毕竟,gulp插件的实现不像grunt插件的实现那么直观. 好吧,于是决定单刀直入了.文中 ...
- Gulp探究折腾之路(I)
前言: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成:使用她,我们不仅可以很愉快的编写代码 ...
- Gulp探究折腾之路(I)2
文/晚晴幽草(简书作者)原文链接:http://www.jianshu.com/p/9768a4dc7cf7著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 前言: gulp是前端开发过 ...
- gulp 编译es6 探究
1.gulp配置: var gulp = require('gulp') var fs = require("fs") var babelify = require('babeli ...
- 如何写一个能在gulp build pipe中任意更改src内容的函数
gulp在前端自动化构建中非常好用,有非常丰富的可以直接拿来使用的plugin,完成我们日常构建工作. 但是万事没有十全十美能够完全满足自己的需求,这时我们就要自己动手写一个小的函数,用于在gulp ...
- gulp思考
Gulp,一个基于流的构建工具. 这是自己写的一个构建的demo,只是一个纯演示的示例,并没有完成什么项目工作.下面根据这个demo介绍一下Gulp. 上代码: gulpfile.js 'use st ...
- 前端构建工具之争——Webpack vs Gulp 谁会被拍死在沙滩上
.table tr>td:nth-child(1){width: 2em !important;padding-left: .6rem !important;padding-right: .6r ...
- Gulp介绍与入门实践
Gulp,一个基于流的构建工具. 这是自己写的一个构建的demo,只是一个纯演示的示例,并没有完成什么项目工作.下面根据这个demo介绍一下Gulp. 上代码: gulpfile.js 'use st ...
随机推荐
- 【JAVA】使用 jedis操作redis——连接、存储数据、切库等
本篇运用Java调用jedis包(jedis在线文档API ),做简单操作实例. 安装jedis 1. 2.9.0 jar 版本下载: jedis-2.9.0.jar 2. 新建项目,添加该驱动包 连 ...
- Python中则正则表达式
http://blog.csdn.net/carolzhang8406/article/details/6335072 http://www.iteedu.com/plang/python/pyred ...
- Java学习---面试基础知识点总结
Java中sleep和wait的区别 ① 这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类. sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线 ...
- Linux学习之路-2017/12/22
第三章 管道符.重定向与环境变量 管道命令符,“|”,作用是将前一个命令的标准输出当作后一个命令的标准输入, 格式:“命令A|命令B” 输入输出重定向, 标准输入,STDIN,文件描述符为0,默认从 ...
- windows Server 2012/2016 路由和远程访问,PPPOE,ADSL,连接接口时出现一个错误,连接被远程计算机终止
经过查询资料,是由mprddm.dll的bug引起的. 修改位置: 将je修改为jmp. 查找修改位置,可参考 前面的RasGetPortUserData的调用,或者 后面的 字符串 64位dll可使 ...
- 开源作业调度框架 - Quartz.NET - Cron表达式测试
昨天简单写了一下如何使用Quzrtz.NET. 那么问题来了,我设置了Cron表达式之后如何知道是表达式是否按照预期的时间执行了呢? 我找到了些Cron表达式工具生成了表达式,确发现它们基本上没有进行 ...
- django复习-2-配置、静态文件与路由
一.配置文件 1. BASE_DIR BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) __file__指当 ...
- [python]关于在python中模块导入问题追加总结
[背景] 最近在写程序时,我使用的eclipse编辑器运行都没有问题,然后部署到自动化环境上却偏偏报找不到相应模块问题,现在对该问题在之前的贴子上追加总结 原帖子:[python]关于python中模 ...
- Netty入门(九)空闲连接以及超时
检测空闲连接和超时是为了及时释放资源.常见的方法是发送消息来测试一个不活跃的连接,通常称为“心跳”. Netty 提供了几个 ChannelHandler 来实现此目的,如下: 下面是 IdleSta ...
- intellij IDEA软件java项目No SDK配置jdk开发,安装IDEA软件步骤
我们在使用intellij idea开发java项目的时候,我们在创建的时候会发现提示No SDK,影响创建和使用项目,我们需要下载和配置需要的JDK 电脑 1我们使用intellij idea创建j ...