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 ...
 
随机推荐
- ejb-jar.xml
			
所有bean类(无论是会话bean还是实体bean)必须实现的最基本的接口是javax.ejb.EnterpriseBean接口. 所有的会话bean必须实现javax.ejb.SessionBean ...
 - List集合的特有功能
			
import java.util.ArrayList; import java.util.List; /** * * List集合的特有功能 * A:添加功能 * void add(int index ...
 - sql点滴45—mysql中group_concat用法
			
group_concat(),手册上说明:该函数返回带有来自一个组的连接的非NULL值的字符串结果.比较抽象,难以理解. 通俗点理解,其实是这样的:group_concat()会计算哪些行属于同一组, ...
 - 【项目 · Wonderland】预则立 && 他山之石
			
[软 工 实 践 · 团 队 作 业] 预则立&&他山之石 标签:WonderLand Part 0 · 简要目录 Part 1 · 团队计划 Part 2 · 团队访谈 Part 3 ...
 - PyQt5---ChangeIcon
			
# -*- coding:utf-8 -*- ''' Created on Sep 13, 2018 @author: SaShuangYiBing ''' import sys from PyQt5 ...
 - jQuery1.9+ 废弃的函数和方法 升级Jquery版本遇到的问题
			
面临问题 很久没关注JQuery了,今天突然想升级一下系统中使用的jquery版本,突然发现,升级JQuery版本到1.9之后出现了很多问题,比如:$.browser is undefined.突然就 ...
 - MongoDB3.2配置文件.md
			
Core Options systemLog Options systemLog: verbosity: <int> quiet: <boolean> traceAllExce ...
 - Python os.md
			
os 便携式访问操作系统的特定功能.os模块提供了对特定平台模块(如posix, nt, mac)的封装, 函数提供的api在很多平台上都可以相同使用, 所以使用os模块会变得很方便. 但不是所有函数 ...
 - apache配置文件:http.conf配置详解
			
Apache的配置文件http.conf参数含义详解 Apache的配置由httpd.conf文件配置,因此下面的配置指令都是在httpd.conf文件中修改.主站点的配置(基本配置) (1) 基本配 ...
 - 随手练——小米OJ 高弗雷勋爵
			
高弗雷勋爵 题目链接:https://code.mi.com/problem/list/view?id=113 这个解法比较暴力,主要需要注意的是一颗子弹 弹死两个及以上的情况. #include & ...