写在前面

本来是想写个如何编写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()

gulp.src()内部实现探究的更多相关文章

  1. 温故而知新 gulp.src 指定数组文件夹

    gulp.src语法是基于这个库来实现的,所以详情请看这个API: https://www.gulpjs.com.cn/docs/api/ https://github.com/isaacs/node ...

  2. Gulp:插件编写入门

    之前挖了个坑,准备写篇gulp插件编写入门的科普文,之后迟迟没有动笔,因为不知道该肿么讲清楚Stream这货,毕竟,gulp插件的实现不像grunt插件的实现那么直观. 好吧,于是决定单刀直入了.文中 ...

  3. Gulp探究折腾之路(I)

    前言: gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成:使用她,我们不仅可以很愉快的编写代码 ...

  4. Gulp探究折腾之路(I)2

    文/晚晴幽草(简书作者)原文链接:http://www.jianshu.com/p/9768a4dc7cf7著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 前言: gulp是前端开发过 ...

  5. gulp 编译es6 探究

    1.gulp配置: var gulp = require('gulp') var fs = require("fs") var babelify = require('babeli ...

  6. 如何写一个能在gulp build pipe中任意更改src内容的函数

    gulp在前端自动化构建中非常好用,有非常丰富的可以直接拿来使用的plugin,完成我们日常构建工作. 但是万事没有十全十美能够完全满足自己的需求,这时我们就要自己动手写一个小的函数,用于在gulp ...

  7. gulp思考

    Gulp,一个基于流的构建工具. 这是自己写的一个构建的demo,只是一个纯演示的示例,并没有完成什么项目工作.下面根据这个demo介绍一下Gulp. 上代码: gulpfile.js 'use st ...

  8. 前端构建工具之争——Webpack vs Gulp 谁会被拍死在沙滩上

    .table tr>td:nth-child(1){width: 2em !important;padding-left: .6rem !important;padding-right: .6r ...

  9. Gulp介绍与入门实践

    Gulp,一个基于流的构建工具. 这是自己写的一个构建的demo,只是一个纯演示的示例,并没有完成什么项目工作.下面根据这个demo介绍一下Gulp. 上代码: gulpfile.js 'use st ...

随机推荐

  1. Hadoop 命令 && Web UI

    0. 说明 Hadoop 命令合集 && Web UI 1. HDFS 命令 [1.0 启动所有 && 关闭所有进程] 相当于启动 HDFS 和 YARN # 启动所有 ...

  2. Python实例---beautifulsoup小Demo

    豆瓣 # coding:utf - 8 from urllib.request import urlopen from bs4 import BeautifulSoup html = urlopen( ...

  3. Skype 服务器客户端策略参数优化

    1.skype通讯录原理 对于skype客户端的通讯录同步,首先说说原理,通讯簿信息是从AD同步的skype前端服务器(每天1:30),在从前端服务器同步的客户端(大概1小时内同步一次). skype ...

  4. ConstraintLayout使用手册

    1. 解决痛点 主要用拖拽 解决嵌套过多 2. 简易使用手册 增加约束 四个角直接拖拽就好了 删除约束 match_constraint 属性 这个属性类似于match_parent,去掉margin ...

  5. 极限编程核心价值:简单(Simplicity)

    写在前面 在编写 ASP.NET Core 项目时,深感项目设计的无力感,在软件设计方面我还有很长的路要走.我一直以来都把代码当作一种艺术的存在,认为自己是个"艺术家",其实就是个 ...

  6. 打开word文档时提示“Microsoft Office Word已停止工作”

    我的电脑(Win10)有Office 2003和2013两个版本,可能由于之前超长待机等原因导致word 2003的文件(.doc)不能正常打开,没次都会提示“Microsoft Office Wor ...

  7. Scala学习之路 (九)Scala的上界和下届

    一.泛型 1.泛型的介绍 泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效地增强程序的适用性,使用泛型可以使得类或方法具有更强的通用性.泛型的典型应用场景是集合及集合中 ...

  8. linux 的常用命令---------第七阶段

       LVM 逻辑卷管理器  -----其作用为 :在线扩容 卷组 vG  (也叫LVM卷组) ------------------→     在此卷组vG上建立  :       逻辑卷组 LV ( ...

  9. metamask-iframe-stream,没成功

    https://github.com/kumavis/iframe-stream/blob/master/test/rebundle.js iframe-stream-其实就是将iframe打包成流 ...

  10. CentOS中安装Azkaban 2.5

    必备软件 yum install git -y 单机安装步骤 git clone https://github.com/azkaban/azkaban.git cd azkaban; ./gradle ...