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 是怎麼把文件加工成流的。

未完待續。。。

相關文章
相關標籤/搜索