Gulp 基礎與原理

JerryC發表於2017-02-28

歡迎來我的部落格閱讀:《Gulp 基礎與原理》

Gulp 概述

Gulp 是基於 NodeJS 的專案,一個用作自動化構建的工具,業界一般用來建造前端的工作流。

它的核心原理其實很簡單,最主要是透過各種 Transform Stream 來實現檔案的處理,然後再進行輸出。Transform Streams 是 NodeJS Stream 的一種,是可讀又可寫的,它會對傳給它的物件做一些轉換的操作。

檔案輸入 → Gulp 外掛處理 → 檔案輸出

原則上,gulp 可以針對檔案做任何有趣、有創造力事情。
而自動化構建,只是大家主要比較喜歡使用的方向。

Gulp 的特點:

  • 自動化 - Gulp 為你的工作流而服務,自動執行那些費事費力任務。
  • 平臺透明 - Gulp 被整合到各種 IDE 中,並且除了 NodeJS 之外,其他如 PHP、.NET、Java 平臺都可以使用 Gulp。
  • 強大生態系統 - 你可以使用 npm 上 2000+ 的外掛來構造你的工作流。
  • 簡單 - Gulp 只提供幾個 API,這可以很快地學習和上手。

使用 Gulp

安裝

$ npm install gulp-cli -g // 全域性安裝 Gulp 命令列工具
$ npm install gulp -D // 在專案中,作為 devDependencies 依賴安裝 gulp複製程式碼

Gulpfile.js

在使用 CLI 工具的時候,會執行該檔案,它是一個可執行的 NodeJS 檔案。原理上,你可以在裡面執行任何 NodeJS 程式碼,然後透過呼叫 gulp 提供的 API,來執行 gulp 任務。
gulpfile.js 檔案一般都會放在專案的根目錄中。

一個使用 gulp-babel 外掛來支援 es2015 語法的案例:

const gulp = require('gulp');
const babel = require('gulp-babel');

gulp.task('default', () => {
    gulp.src('src/app.js')
        .pipe(babel({
            presets: ['es2015']
        }))
        .pipe(gulp.dest('dist'));
});複製程式碼

基本概念與原理

瞭解這些概念,對於瞭解 Gulp 的工作原理,和 API 的使用是很有幫助的。

認識 Glob

Glob 是一種用來匹配路徑與檔案的模式。有點類似於正規表示式,但是語法又有點差異。
這種模式,被廣泛用於命令列、Shell 等場景,大家熟悉的 .gitignore 檔案也是使用這種模式。

各大語言都有對於 Glob 的實現,例如 Go 和 PHP 的 Glob 函式,Python 中的 glob 模組。
而 NodeJS 的實現是 minimatch, 而在 Gulp 原始碼中,就用了對 minimatch 進行封裝的 node-glob 模組。
Gulp 的 API gulp.watchgulp.src 都有用到 Glob 來匹配對應的路徑和檔案。
下面是部分語法:

  • *
    匹配該路徑段中 0 個或多個任意字元,
    如:js/*.js, 匹配 js 目錄下的所有 js 檔案
  • ?
    匹配該路徑段中 1 個任意字元,
    如:js/?.js,匹配 js 目錄下所有名字只有 1 個字的 js
  • [...]
    匹配該路徑段中在指定範圍內字元,
    如:js/a[0-3].js,匹配 js 目錄下 a 開頭,第二個字元為 0-3 之間( 包括0和3 )的 js( a03.js不能被匹配到 )

  • !(pattern|pattern|pattern)
    匹配除所給出的模型以外的情況,
    如:js/!(a|b).js,匹配 js 目錄下名字中不包含 a ,也不包含 b 的所有檔案.

  • ?(pattern|pattern|pattern)
    匹配所給出的模型中的 0 個或任意 1 個,
    如:js/?(a|a2|b).js, 匹配 js 目錄下 a.js , a2.js , b.js

  • +(pattern|pattern|pattern)
    匹配所給出的模型中的 1 個或者多個,
    如:js/+(a|a1|b).js, 匹配 js 目錄下 a.js , a1.js , b.js , 或者 a, a1, b 這幾個字元的組合的 js , 比如 ab.js

  • *(pattern|pattern|pattern)
    匹配所給出的模型中的 0 個或多個或任意個的組合.
    如:js/*(a|a1|b).js,匹配 js 目錄下 a.js, a1.js, b.js 或者 a, a1, b這幾個字元的組合的 js , 比如 ab.js

  • @(pattern|pat*|pat?erN)
    匹配所給出的模型中的任意 1 個,
    如:js/@(a|a1|b), 匹配 js 目錄下的 a.js, a1.js, b.js

  • **
    * 一樣可以匹配任何內容,但 **不僅匹配路徑中的某一段,而且可以匹配 a/b/c 這樣帶有 / 的內容,所以,它還可以匹配子資料夾下的檔案.
    如:js/**/*.js,匹配 js 目錄下及子資料夾中所有的 js 檔案。

更多 Glob 的知識和語法,可以參考:
Glob - Wiki
Glob Primer

認識 Vinyl

Vinyl 是 Gulp 自創的一種用來描述一個虛擬檔案的類,其中主要包括檔案的內容和檔案的路徑兩大資訊。vinyl 模組,只是提供了一個類,而實現卻交由 vinyl-fs

Vinyl-fs,它主要的工作是接受 glob 模式的引數,然後讀取匹配的檔案。然後利用 Vinyl 製作一個 Transform Stream,稱為 Vinyl Stream 物件,並返回。

在 Gulp 中的 API gulp.srcgulp.watchgulp.dest 都返回一個 Vinyl Stream 例項物件。Vinyl Stream 例項之間可以透過管道( vinyl1.pipe(vinyl2) )的形式來互相傳輸資料。

從 Gulp 的 原始碼 中也能看出,這三個 API 都是由 vinyl-fs 提供全部的實現。

再一點是,從這兩個模組的實現來看,Gulp 是把檔案內容以 Buffer 的形式讀到記憶體中,然後再進行處理的。

認識 Orchestrator

Orchestartor,為 gulp.task 提供了全部實現,這可以從 原始碼 中看出。
它為 Gulp 提供了任務相關的功能,包括任務註冊、任務執行以及相對應的任務進度、錯誤監控等功能。

Orchestartor 模組,只提供了一個 Orchestartor 類,該類的例項維護著一個 tasks 陣列,該陣列的內容就是一個我們使用 gulp.task 時註冊的函式列表,以及函式的依賴和名字。
透過 原始碼 中,可以看到 tasks 的資料結構:

...
this.tasks[name] = {
  fn: fn,   // 任務的函式體
  dep: dep,   // 任務所依賴的其他任務名稱
  name: name  // 該任務的名稱
};
...複製程式碼

Gulp 核心 API

  • gulp.src:獲取檔案
  • gulp.dest:寫入檔案
  • gulp.tasks:註冊任務
  • gulp.watch:監控檔案的改動

gulp.src

gulp.src( globs [, options] )

接收一個 globs 模式的物件,可以是 Array 或者 String,返回一個 Vinyl Stream 例項。
而 options 有下面的值:

  • buffer - Boolean, 控制 file.contents 是返回 buffer 還是 stream。
  • read - Boolean,控制是否讀取檔案,如果 false,則 file.contentsnull
  • base - String,控制 glob 的 base,預設值是 glob 所有表示式的前置,例如 client/js/**/*.js, base 值就為 client/js/。而 glob 在儲存輸出路徑的時候,取的是 base 之後的路徑。所以可以透過該值,來進行輸出路徑的改寫。

gulp.dest

gulp.dest( path [, options] )

接收輸出路徑,返回一個 Vinyl Stream 例項。
而 options 有以下的值:

  • cwd - String, 預設值 process.pwd(),輸出目錄的 cwd 引數,只在所給的輸出目錄是相對路徑時候有效。
  • mode - String,八進位制許可權字元,用以定義所有在輸出目錄中所建立的目錄的許可權。

gulp.task

gulp.task( name [, deps ], fn )

定義一個使用 Orchestrator 實現的任務(task)。
引數的描述如下:

  • name - 任務名稱
  • deps - 是當前定義的任務需要依賴的其他任務,為一個陣列。當前定義的任務會在所有依賴的任務執行完畢後才開始執行。如果沒有依賴,則可省略這個引數
  • fn - 為任務函式,我們把任務要執行的程式碼都寫在裡面。該引數也是可選的。

gulp.watch

gulp.watch( glob [, opts ], tasks )
or
gulp.watch( glob [, opts, cb ] )

用來監視檔案的變化,當檔案發生變化後,我們可以利用它來執行相應的任務。
各引數的描述如下:

  • glob - 為要監視的檔案 Glob 匹配模式。
  • opts - 為一個可選的配置物件。
  • tasks - 為檔案變化後要執行的任務,為一個陣列

常用外掛

更多外掛,可以搜尋官方外掛庫

相關文章