Gulp Source Code Analysis

Gulp介绍

Gulp是一款非常方便的前端脚手架工具,为取代Grunt复杂的配置项而生,基于流模式(Stream Mode),快速配置,易上手

利用gulpfile.js配置你的任务,简化手工操作

gulp.task('mytask', function() {
  // do some thing...
})

然后你可以这样完成你配置的任务

gulp mytask

是不是很方便?来看看他怎么实现的吧

Gulp Source Code

clone 下来gulp@3.9.0的repo,一起来看看

Gulp repo的目录里面,主要包含以下几个模块

- gulp
  |- bin
    |- gulp.js
  |- lib
    |- completion.js
    |- taskTree.js
  |- node_modules
    |- orchestrator
    |- vinyl-fs
    |- liftoff
    ...
  |- index.js
  ...

下面的剖析将根据这个目录进行

index.js

bin是global tool,lib里面是用到的不同shell下的补全,和任务依赖树
index.js里是一个Gulp的instance,包含了Gulp的5个接口task, run, src, dest, watch,在4.0里run已经deprecated了

来看看这几个接口的功能

var Orchestrator = require('orchestrator');
var vfs = require('vinyl-fs');


function Gulp() {
  Orchestrator.call(this);
}
util.inherits(Gulp, Orchestrator);


Gulp.prototype.task = Gulp.prototype.add;
Gulp.prototype.run = function() {
  // `run()` is deprecated as of 3.5 and will be removed in 4.0
  // Use task dependencies instead

  // Impose our opinion of "default" tasks onto orchestrator
  var tasks = arguments.length ? arguments : ['default'];

  this.start.apply(this, tasks);
};

Gulp.prototype.src = vfs.src;
Gulp.prototype.dest = vfs.dest;
Gulp.prototype.watch = function(glob, opt, fn) {
  if (typeof opt === 'function' || Array.isArray(opt)) {
    fn = opt;
    opt = null;
  }

  // Array of tasks given
  if (Array.isArray(fn)) {
    return vfs.watch(glob, opt, function() {
      this.start.apply(this, fn);
    }.bind(this));
  }

  return vfs.watch(glob, opt, fn);
};

首先有两个主要的dependency: Orchestratorvinyl-fs

Orchestrator

readme.md里解释如下

A module for sequencing and executing tasks and dependencies in maximum concurrency

能看出来这个是一个并发库,为gulp提供task接口,所以gulp里的task是并行的

Orchestrator 提供了 addstart 两个接口,Gulp 继承了 Orchestrator, 但 start 接口并没有被暴露给用户,
你从已经deprecated的 run 接口能看出,实际上,你最后在命令行里gulp mytask所执行的任务,就相当于执行了

Orchestrator.start('mytask')

Vinyl-fs

Vinyl adapter for the file system

这又是什么呢?看看他的依赖库Vinyl的解释

Virtual file format

依旧云里雾里,但里面给了一个解释的链接

Vinyl is a very simple metadata object that describes a file. When you think of a file, two attributes come to mind: path and contents. These are the main attributes on a Vinyl object. A file does not necessarily represent something on your computer’s file system. You have files on S3, FTP, Dropbox, Box, CloudThingly.io and other services. Vinyl can be used to describe files from all of these sources.

意思大致是说Vinyl是一个metadata object,用于文件描述,主要包含pathcontents这两个文件属性,还可能是你云盘或者dropbox上面的xxx…

而 vinyl-js 主要针对File System,你可以理解为一个 Special File Object

Vinyl-js 提供了 srcdest 两个原生接口,和一个经过封装的 watch 接口,封装只是对传入的参数顺序进行合理化的调整,如果callback里是一个callback function那就当正常做回调,如果是一个task array,那么先执行它们。

这也造就了gulp蛋疼的任务配置,你必须有一个比较合理的任务序列安排,写好每个task 的 dependency

bin/gulp.js

这个主要是gulp暴露的命令行接口,他也应用了一个叫 Lift-Off 的工具

Launch your command line tool with ease.

就是帮你快速建立命令工具(CLI Tool)的,可以帮助你寻找这个工具的config-file
而且你在gulp运行多个任务的时候可以看见它里面打出来的log,能帮助你判断gulp的工作

举个栗子

// gulpfile.js

gulp.task('mytask', ['stylus', 'jade', 'serve'])

// in your shell

gulp mytask
...

你看见的结果可能是这样的

[17:23:31] Starting 'stylus'...
[17:23:32] Starting 'jade'...
[17:23:32] Starting 'serve'...
[17:23:32] Finished 'serve' after 7.18 ms
livereload[tiny-lr] listening on 35729 ...
folder "dist" serving at http://localhost:8000
[17:23:33] Finished 'jade' after 868 ms
[17:23:33] Finished 'stylus' after 1.1 s

几个任务同时被开启,但是结束时间不同,不是一个个顺序执行的

那就说到这吧,如有问题欢迎纠错