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 ...
随机推荐
- 存储过程使用 in 添加多个参数的情况处理方式【转】
原文连接:http://www.jb51.net/article/41472.htm -->情景 ① 通过刚才的SQL递归方式,我们已经可以将一个组织机构和其全部下级单位查询出来:假设每个组织机 ...
- C# 泛型约束 xxx<T> Where T:约束(一)
泛型约束 代码举例 发现我们游戏的代码中,主程写了很多类似这样的代码: public static T CreateObject<T>(out int objectId) where T ...
- python Pipe 双管道通信
管道:是python多进程中一种交换数据的方式 from multiprocessing import Process,current_process,Queue,Pipe import time i ...
- 嵌入式开发板LInux更新系统、安装软件、下载资源碰到的问题
CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none 先同步系统时间 shell 输入命令 date 如果显示的时间跟你本地时间不一样,先设 ...
- JavaScript中的单例模式
单例模式 在JavaScript中,单例(Singleton)模式是最基本又最有用的模式之一.这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一的变量进行访问.确保单例 ...
- 修改Lotus notes默认浏览器
notes收到的邮件里面的链接,点了以后会自动在notes里打开,但notes很弱,加载页面总是有问题.怎么才能让他默认用ie浏览器打开呢,怎么设置,请教达人. 右下角有个office->Edi ...
- linux问题整理
linux常用命令 //1.Linux如何查询进程?杀死一个进程? //2.文件权限,改变权限命令 //3.Linux中查看服务的命令 //4.linux查看内存 //5.查看磁盘空间的命令,查看文件 ...
- 十分钟教你使用NoteExpress
http://www.a-site.cn/article/761794.html 如果你正走在读研的路上,不管是什么专业,日常生活中都少不了读文献.读文献和读文献. 与其等到文献堆积如山,给阅读和使用 ...
- 剑指offer.从未到头打印链表
输入一个链表的头结点,按照 从尾到头 的顺序返回节点的值.返回的结果用数组存储. 样例 输入:[2, 3, 5] 返回:[5, 3, 2] 1.使用栈. class Solution { public ...
- 前端:background 设置
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...