gulp原始碼解析(2):vinyl-fs

發表於2017-02-03

上一篇文章我們對 Stream 的特性及其介面進行了介紹,gulp 之所以在效能上好於 grunt,主要是因為有了 Stream 助力來做資料的傳輸和處理。

那麼我們不難猜想出,在 gulp 的任務中,gulp.src 介面將匹配到的檔案轉化為可讀(或 Duplex/Transform)流,通過 .pipe 流經各外掛進行處理,最終推送給 gulp.dest 所生成的可寫(或 Duplex/Transform)流並生成檔案。

本文將追蹤 gulp(v4.0)的原始碼,對上述猜想進行驗證。

o_div

為了分析原始碼,我們開啟 gulp 倉庫下的入口檔案 index.js,可以很直觀地發現,幾個主要的 API 都是直接引用 vinyl-fs 模組上暴露的介面的:

因此瞭解 vinyl-fs 模組的作用,便成為掌握 gulp 工作原理的關鍵之一。需要留意的是,當前 gulp4.0 所使用的 vinyl-fs 版本是 v2.0.0

vinyl-fs 其實是在 vinyl 模組的基礎上做了進一步的封裝,在這裡先對它們做個介紹:

一. Vinyl

Vinyl 可以看做一個檔案描述器,通過它可以輕鬆構建單個檔案的後設資料(metadata object)描述物件。依舊是來個例子簡潔明瞭:

上述程式碼會列印兩個File檔案物件:

561179-20170129182956987-2110917733

簡而言之,Vinyl 可以建立一個檔案描述物件,通過介面可以取得該檔案所對應的資料(Buffer型別)、cwd路徑、檔名等等:

列印結果:

561179-20170129192621003-828746978

更全面的 API 請參考官方描述文件,這裡也對 vinyl 的原始碼貼上解析註釋:

o_div

二. Vinyl-fs

Vinyl 雖然可以很方便地來描述一個檔案、設定或獲取檔案的內容,但還沒能便捷地與檔案系統進行接入。

我的意思是,我們希望可以使用萬用字元的形式來簡單地匹配到我們想要的檔案,把它們轉為可以處理的 Streams,做一番加工後,再把這些 Streams 轉換為處理完的檔案。

Vinyl-fs 就是實現這種需求的一個 Vinyl 介面卡,我們看看它的用法:

如上方程式碼所示,Vinyl-fs 的 .src 介面可以匹配一個萬用字元,將匹配到的檔案轉為 Vinyl Stream,而 .dest 介面又能消費這個 Stream,並生成對應檔案。

這裡需要先補充一個概念 —— .src 介面所傳入的“萬用字元”有個專有術語,叫做 GLOB,我們先來聊聊 GLOB。

gulp原始碼解析(2):vinyl-fs

o_div

GLOB 可以理解為我們給 gulp.src 等介面傳入的第一個 pattern 引數的形式,例如“./js/**/*.js”,另外百度百科的“glob模式”描述是這樣的:

所謂的 GLOB 模式是指 shell 所使用的簡化了的正規表示式:
⑴ 星號(*)匹配零個或多個任意字元;
⑵ [abc]匹配任何一個列在方括號中的字元(這個例子要麼匹配一個 a,要麼匹配一個 b,要麼匹配一個 c);
⑶ 問號(?)只匹配一個任意字元;
⑷ 如果在方括號中使用短劃線分隔兩個字元,表示所有在這兩個字元範圍內的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的數字)。

在 vinyl-fs 中,是使用 glob-stream <v5.0.0>通過演算法minimatch來解析 GLOB 的,它會拿符合上述 GLOB 模式規範的 pattern 引數去匹配相應的檔案,:

而 glob-stream 又是藉助了 node-glob 來匹配檔案列表的:

列印結果:

561179-20170131133538026-1210507743

這裡也貼下 glob-stream 的原始碼註解:

留意通過 glob-stream 建立的流中,所寫入的資料:

是不像極了 Vinyl 建立檔案物件時可傳入的配置。

o_div

我們回過頭來專注 vinyl-fs 的原始碼,其入口檔案如下:

下面分別對這三個對外介面(也直接就是 gulp 的對應介面)進行分析。

2.1 gulp.src

該介面檔案為 lib/src/index.js,程式碼量不多,但引用的模組不少。

主要功能是使用 glob-stream 匹配 GLOB 並建立 glob 流,通過 through2 寫入 Object Mode 的 Stream 去,把資料初步加工為 Vinyl 物件,再按照預設項進行進一步加工處理,最終返回輸出流:

561179-20170131165907308-604032787

程式碼主體部分如下:

這裡有個 symlink 的概念 —— symlink 即  symbolic link,也稱為軟鏈(soft link),它使用了其它檔案或資料夾的連結來指向一個檔案。一個 symlink 可以連結任何電腦上的任意檔案或資料夾。在 Linux/Unix 系統上,symlink 可以通過 ln 指令來建立;在 windows 系統上可以通過 mklink 指令來建立。

更多 symlink 的介紹建議參考 wiki —— https://en.wikipedia.org/wiki/Symbolic_link

o_div

2.2 gulp.dest

該介面檔案為 lib/dest/index.js,其主要作用自然是根據 src 介面透傳過來的輸出流,生成指定路徑的目標檔案/資料夾:

接前文的流程圖:

561179-20170131203838354-1532578800

至此我們就搞清楚了 gulp 的 src 和 dest 是怎樣運作了。另外 gulp/vinyl-fs 還有一個 symlink 介面,其功能與 gulp.dest 是一樣的,只不過是專門針對 symlink 的方式來處理(使用場景較少),有興趣的同學可以自行閱讀其入口檔案 lib/symlink/index.js

本文涉及的所有示例程式碼和原始碼註釋檔案,均存放在我的倉庫(https://github.com/VaJoy/stream/)上,可自行下載除錯。共勉~

相關文章