loader承擔的是翻譯官的職責,利用其彌補了讓webpack只能理解JavaScript和JSON文件的問題,從而能夠處理其它類型的文件,因此loader對webpack的重要性不言而喻,因此學習構建一個loader是學習webpack的必經之路。在學習編寫一個loader以前,要明確一下loader的職責: 其職責是單一的,只須要完成一種轉換。下面將逐步闡述選擇loader開發中的幾個關鍵點並實現一個loader。
loader是一個CommonJs風格的函數,接收輸入的source後可經過同步或異步的方式進行處理,而後將內容進行輸出。
同步loader指的是同步的返回轉換後的內容。,因爲是在Node.js這樣的單線程環境,因此轉換過程會阻塞整個構建,構建緩慢,不適用於耗時較長的環境中。對於同步loader,主要有兩種方法返回轉換後的內容:return和this.callback.
利用return可直接返回轉換後結果。css
module.exports = function(source, map, meta){ // ... // output爲處理後結果 return output; }
該方法相比於return更加靈活,其參數主要有四個:html
this.callback( err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any );
(1)第一個參數爲沒法轉換原內容是,Webpack會返回一個Error。<br/>
(2)第二個參數即爲通過轉換後的內容(爲輸出的內容)。<br/>
(3)指與編譯後代碼所映射的源代碼,便於調試。爲了在此loader中獲取該sourceMap,則須要在建立的webpack作一下配置(以js爲例,babel-loader會將基礎ES6語法進行轉換爲ES5,經過devtool能夠開啓source-map):node
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.js$/, use: [ 'test-loader',// 該loader即爲本身構建的loader { loader: 'babel-loader', options: { presets: [ '@babel/preset-env' ] } } ] } ] }, devtool: 'eval-source-map', }
(4)能夠是任何東西,輸出該參數,便可在下一個loader中獲取並使用,例如經過各loader之間共享通用的AST,加速編譯時間。webpack
利用this.callback可返回傳遞多參數的結果。
module.exports = function(source, map, meta) { // 處理後得到的結果output const output = dealOperation(source); this.callback(null, output, map, meta); }
同步loader只適合於計算量小,速度快的場景,可是對於計量算大、耗時比較長的場景(例如網絡請求),使用同步loader會阻塞整個構建過程,致使構建速度變慢,採用異步loader便可避免該問題。對於異步loader,使用this.async()能夠獲取到callback函數,該函數參數和同步loader中this.callback參數一致。
module.exports = function(content, map, meta) { // 獲取callback函數 const callback = this.async(); // 用setTimeout模擬該異步過程 setTimeout(() => { // 處理後得到的結果output const output = dealOperation(source); callback(null, output, map, meta); }, 100) }
默認狀況下,資源文件經轉化後都是UTF-8格式編碼的字符串,可是對於圖片這樣的文件通過轉化後是二進制格式的內容,爲了讓loader支持接收二進制資源,須要設置raw(以圖片資源爲例進行展現)
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.(png|jpg|gif)$/, use: [ 'url-loader', 'raw-test-loader',// 本身的loader ] } ] } }
// raw-test-loader.js module.exports = function(source, map, meta) { // 處理輸入的資源 const output = dealOperation(source); return output; } // 經過該屬性告訴webpack該loader是否須要二進制數據 module.exports.raw = true;
對於webpack配置中,loader每每有一些options參數,對於本身編寫的loader中爲了獲取options參數,官方推薦使用loader-utils包,利用該包便可獲取options中參數,而後在loader中進行處理。
const loaderUtils = require('loader-utils'); module.exports = function (source, map, meta){ // 獲取options const options = loaderUtils.getOptions(this); const output = dealOperation(source); return output; }
對於轉換操做須要大量的計算,很是耗時,每次從新構建會讓構建過程變的很是緩慢。webpack會默認緩存全部loader的處理結果,即要處理文件和其相關依賴沒發生變化就會利用其緩存(注意loader除了this.addDependency裏指定的依賴外,不該該有任何外部依賴)。經過this.cacheable可控制其是否進行緩存。
module.exports = function(source, map, meta) { // 關閉緩存 this.cacheable(false); return source; }
本節是loader實戰,編寫了一個用於字母大小寫轉換的loader,利用該loader可以實現將txt文件中字母的大小寫轉換,其loader內容及webpack.config.js相關配置以下所示(詳細代碼見 github上代碼)
// format-letters-loader.js const loaderUtils = require('loader-utils'); const Lowercase2Uppercase = 'L2U'; const Uppercase2Lowercase = 'U2L'; module.exports = function (source, map, meta) { let output = ''; // 獲取options const options = loaderUtils.getOptions(this); const { formatType } = options; switch(formatType) { case Lowercase2Uppercase: { output = source.toUpperCase(); break; } case Uppercase2Lowercase: { output = source.toLowerCase(); break; } default: { output = source; } } this.callback(null, output, map, meta); };
// webpack.config.js module.exports = { // ... module: { rules: [ { exclude: /\.(css|js|html|png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { name: '[name].[ext]', outputPath: 'asset', } }, { loader: 'format-letters-loader', options: { formatType: 'U2L' } }, ] } ] }, // 解析loader包是設置模塊如何被解析 resolveLoader: { modules: ['./node_modules', './loader'],// 告訴 webpack 解析loader時應該搜索的目錄。 }, }
注:本文只是起到拋磚引玉的做用,但願各位大佬多多指點。git
相關章節<br/>
圖解Webpack————基礎篇
圖解Webpack————優化篇歡迎你們關注公衆號(回覆「深刻淺出Webpack」獲取深刻淺出Webpack的pdf版本,回覆「webpack04」獲取本節的思惟導圖」)
github