zoukankan      html  css  js  c++  java
  • 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()

  • 相关阅读:
    如何使用Flexbox和CSS Grid,实现高效布局
    最常用的四种大数据分析方法
    如何编写更好的SQL查询:终极指南-第三部分
    如何编写更好的SQL查询:终极指南-第二部分
    如何编写更好的SQL查询:终极指南-第一部分
    ES2017异步函数现已正式可用
    相对传统桌面设计器,在线报表设计器价值何在?
    如何实现报表设计中的高精度报表套打?
    2017年前端开发工具趋势
    Angular2 VS Angular4 深度对比:特性、性能
  • 原文地址:https://www.cnblogs.com/chyingp/p/gulp-src-implement.html
Copyright © 2011-2022 走看看