Gulp 学习小结

gulp 任务怎么写

const gulp = require('gulp');

gulp.task('default', function() {
    gulp.src('js/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('dist'));
});
复制代码

注意:默认的,task 将以最大的并发数执行,gulp 会一次性运行全部的 task 而且不作任何等待。若是你想要建立一个序列化的 task 队列,并以特定的顺序执行,你须要作两件事:node

  • 给出一个提示,来告知 task 何时执行完毕
  • 而且再给出一个提示,来告知一个 task 依赖另外一个 task 的完成
var gulp = require('gulp');

// 返回一个 callback,所以系统能够知道它何时完成
gulp.task('one', function(cb) {
    // 作一些事 -- 异步的或者其余的
    cb(err); // 若是 err 不是 null 或 undefined,则会中止执行,且注意,这样表明执行失败了
    这里也可使用 return 代替 cb()
});

// 定义一个所依赖的 task 必须在这个 task 执行以前完成
gulp.task('two', ['one'], function() {
    // 'one' 完成后
});

gulp.task('default', ['one', 'two']);
复制代码

gulp 核心知识

我的认为了解 gulp 的核心知识,主要从三个方面来了解:git

  • 文件系统
  • 任务管理

上面三点分别对应了gulp的 pipe(这样写不太严谨,由于pipe是流的api),src和task 这三个API,了解了这三部分,gulp的使用,插件的编写都会比较驾轻就熟。github

流(Stream)

说实话到如今也没有对流的全部知识十分了解,以前查阅了一些 node 相关的书,感受不少书写的东西和例子都很浅,后来看了一些文章,算是了解了一点皮毛。这是网传的学习流必看的文章,有兴趣的能够看看。gulp

stream-handbook的完整中文版本api

本文默认读者对流有了必定的了解,所以这里对流的种类,相关API和使用不会说的很详细。并发

流的好处

stream不会占用大量内存。例如 fs.readFile 在读取文件时,会把整个文件读取到内存当中,当文件很大时,会很占用内存,甚至会超出v8的内存限制,致使程序退出。流则是读一部分,写一部分,并且可以充分地利用 Buffer 不受 V8 内存控制的特色,利用堆外内存完成高效地传输。异步

gulp 中的流

gulp 内部使用了 through2 封装好的 transform 流(后面会展开说),这一步是在 gulp.src 的时候完成的,所以咱们能够看到这样的代码:函数

gulp.src(xxx).pipe(xxx)...
复制代码

我我的理解的 pipe 就是一根水管,左边是可读流(readStream),右边是可写流(writeStream)。正由于 gulp 帮咱们封装好了流,所以,咱们能够直接使用 pipe 这个 api 来传输数据。oop

gulp 插件的编写

学习插件的编写能让咱们对gulp中流的使用有更深刻的认识,有的时候可能咱们须要一些定制话的功能来处理文件,这时就须要本身编写gulp的插件了,咱们先来看一个官方的例子:学习

点击打开
var through = require('through2');
var gutil = require('gulp-util');
var PluginError = gutil.PluginError;

// 常量
const PLUGIN_NAME = 'gulp-prefixer';

function prefixStream(prefixText) {
  var stream = through();
  stream.write(prefixText);
  return stream;
}

// 插件级别函数 (处理文件)
function gulpPrefixer(prefixText) {

  if (!prefixText) {
    throw new PluginError(PLUGIN_NAME, 'Missing prefix text!');
  }
  prefixText = new Buffer(prefixText); // 预先分配

  // 建立一个让每一个文件经过的 stream 通道
  return through.obj(function(file, enc, cb) {
    if (file.isNull()) {
      // 返回空文件
      cb(null, file);
    }
    if (file.isBuffer()) {
      file.contents = Buffer.concat([prefixText, file.contents]);
    }
    if (file.isStream()) {
      file.contents = file.contents.pipe(prefixStream(prefixText));
    }

    cb(null, file);

  });

};

// 暴露(export)插件主函数
module.exports = gulpPrefixer;
复制代码

对于 gulp 插件的编写我认为这两点是比较重要的:

  • through2 作了什么
  • through2.obj 中的 function 怎么写

带着这些疑问,咱们先看下 through2 的源码:源码地址

点击打开
// 前面的代码建立了一个 Transform 流,并给这个流添加了一个 destroy 方法
function through2 (construct) {
  return function (options, transform, flush) {
    if (typeof options == 'function') {
      flush     = transform
      transform = options
      options   = {}
    }

    if (typeof transform != 'function')
      transform = noop

    if (typeof flush != 'function')
      flush = null

    return construct(options, transform, flush)
  }
}

module.exports.obj = through2(function (options, transform, flush) {
  var t2 = new DestroyableTransform(Object.assign({ objectMode: true, highWaterMark: 16 }, options))

  t2._transform = transform

  if (flush)
    t2._flush = flush

  return t2
})

复制代码
看完代码,咱们应该能回答上面的两个问题了。through2 会返回一个封装好的带有 destroy 方法的 Transform 流,而咱们使用时传入的 function,实际是 Transform 流的 _transform 方法,用于数据的处理。这里咱们有几点要注意一下:
  • 编写插件时要使用 through2.obj 方法
  • _transform 方法最后要执行一下 callback 后才能接收下一个数据块

文件系统

文件系统这个名字说着有点悬乎,其实这部分就是介绍一下 gulp.src 是怎么把文件加工成流的。

未完待续。。。

相关文章
相关标签/搜索