如何開發一個 Webpack Loader ( 一 )

發表於2016-01-14

webpack

最近,專案用了 React,配套使用了 Webpack,畢竟熱替換(react-hot-loader)吸引力確實高,開發模式下使用 webpack 構建其實也夠用,並且相對 gulp-webpack 來說,模組的編譯等待時間大大縮小,這是生命啊! 釋出時,藉助 gulp 來進行其他方面的處理,如合圖,打包等。或許把這些邊幅修一修、支援下,Webpack 估計就要逆天了吧?

仰望天空,還是腳踏實地,Webpack 雖非新鮮之物,但也沒有多成熟。對應的 Plugin 及 Loader 的量並不多,還是有很多輪子沒造,很多坑沒踩呢。從原始碼中似乎看到了一些可能在接下來會暴漏出來的新介面,想想還有點小激動,期待下 Webpack2 吧。

碎碎唸完畢,以下講講如何開發一個基礎的 Webpack Loader 及一些心得。

 

1 開發 Webpack Loader 前須知

Loader 是支援鏈式執行的,如處理 sass 檔案的 loader,可以由 sass-loader、css-loader、style-loader 組成,由 compiler 對其由右向左執行,第一個 Loader 將會拿到需處理的原內容,上一個 Loader 處理後的結果回傳給下一個接著處理,最後的 Loader 將處理後的結果以 String 或 Buffer 的形式返回給 compiler。

這種鏈式的處理方式倒是和 gulp 有點兒類似,固然也是希望每個 loader 只做該做的事,純粹的事,而不希望一籮筐的功能都整合到一個 Loader 中。

另一方面,雖然鏈式之間可以依賴其前一個Loader所返回的結果來執行自己的內容。但這並不支援兩個 Loader 之間進行資料交流的做法,一個標準的 Loader 應該是要求著 強獨立性、以及輸入什麼,就輸出什麼的可預見性。

2 Webpack Loader 基礎

官網說了,A loader is a node module exporting a function.

既然是 node module,那麼基本的寫法可以是

如果你所寫的 Loader 需要依賴其他模組的話,那麼同樣以 module 的寫法,將依賴放在檔案的頂部宣告,讓人清晰看到

上面使用返回 return 返回,是因為是同步類的 Loader 且返回的內容唯一,如果你希望將處理後的結果(不止一個)返回給下一個 Loader,那麼就需要呼叫 Webpack 所提供的 API。 一般來說,構建系統都會提供一些特有的 API 供開發者使用。Webpack 也如此,提供了一套 Loader API,可以通過在 node module 中使用 this 來呼叫,如 this.callback(err, value…),這個 API 支援返回多個內容的結果給下一個 Loader 。

以上的內容,稍總結下

  • 從右到左,鏈式執行
  • 上一個 Loader 的處理結果給下一個接著處理
  • node module 寫法
  • module 依賴
  • return && this.callback()

而實際上,掌握上面所介紹的內容及思想,就可以開始寫一個簡單的 Loader 了,不是嗎? 由上所說的,在你的 Loader 中,你可以拿到需要處理的檔案內容,並且知道了處理後的結果應該怎麼去返回,在中間部分,你可以以正常使用 node 的姿態對內容進行怎樣的處理,Do Whatever You Want,Loader 沒有其他特殊要求。

3 如何開發更好用的 Webapck Loader

上半部分的介紹雖然確實能搭建起一個普通的 Loader 了,但這樣就夠了嗎?

❶ 快取

從提高執行效率上,如何處理利用快取是極其重要的。 Mac OS 會讓記憶體充分使用、儘量佔滿來提高互動效率。回到 Webpack,Hot-Replace 以及 React Hot Loader 也充分地利用快取來提高編譯效率。 Webpack Loader 同樣可以利用快取來提高效率,並且只需在一個可快取的 Loader 上加一句 this.cacheable(); 就是這麼簡單

很多 Loader 都是可以快取的,但也有例外。可以快取的 Loader 需要具備可預見性,不變性等等。

❷ 非同步

非同步並不陌生,當一個 Loader 無依賴,可非同步的時候我想都應該讓它不再阻塞地去非同步。在一個非同步的模組中,回傳時需要呼叫 Loader API 提供的回撥方法 this.async(),使用起來也很簡單

❸ 認識更多的 Loader

pitching Loader

前面所述的 Loader 從右到左鏈式執行。這種說法實際說的是 Loader 中 module.exports 出來的執行方法順序。在一些場景下,Loader 並不依賴上一個 Loader 的結果,而只關心原輸入內容。這時候,從左到右執行並沒有什麼問題。在 Loader 的 module 中,可使用 module.exports.pitch = function(); pitch 方法在 Loader 中便是從左到右執行的,並且可以通過 data 這個變數來進行 pitch 和 normal 之間傳遞。

具體的實踐可以檢視 style-loader,裡面就有使用到 pitch。

raw loader

預設的情況,原檔案是以 UTF-8 String 的形式傳入給 Loader,而在上面有提到的,module 可使用 buffer 的形式進行處理,針對這種情況,只需要設定 module.exports.raw = true; 這樣內容將會以 raw Buffer 的形式傳入到 loader 中了

❹ 善用 Loader 中的 this

Loader API 將提供給每一個 Loader 的 this 中,API 可以讓我們的呼叫方式更加地方便,更加靈活。

data  pitch loader 中可以通過 data 讓 pitch 和 normal module 進行資料共享。

query  則能獲取到 Loader 上附有的引數。 如 require(“./somg-loader?ls”); 通過 query 就可以得到 “ls” 了。

emitFile  emitFile 能夠讓開發者更方便的輸出一個 file 檔案,這是 webpack 特有的方法,使用的方法也很直接

在 file-loader 中有呼叫到 this.emitFile(url, content); 這個方法,具體可以檢視其原始碼瞭解。

更多的 API 就不在此 一 一 說明了,建議檢視官網文件瞭解。最後推薦一個工具模組 loader-utils,大多數的 Loader 都會用上它來解析或者使用它提供的一些 util 方法,很方便。

話不多說

針對 Loader 的基礎介紹大致就到這了,不多,希望這篇文章能夠對 Webpack Loader 有一個大致的瞭解。 更多進階的方案及實戰經驗容我再整理整理,遲些輸出。

相關文章