自定義loader基本方法,節選自 webpack實戰。
1、loader初始化
如果已知loader無法滿足我們的需求的時候,就需要動手開發一個定制的loader,我們將實現一個loader:它會為所有的JS文件啟用嚴格模式,也就是說它會在文件頭部加上如下代碼:
"use strict";
- 創建一個force-strict-loader目錄,然后在該目錄下執行npm初始化命令。npm init -y
- 創建index.js,也就是loader的主體
module.exports = function(content){ var useStrictPrefix = '\'use strict\'; \n\n'; return useStrictPrefix + content; }
現在我們可以在webpack工程中安裝並使用這個loader了
- npm install <path-to-loader>/force-strict-loader;在webpack工程目錄下使用相對路徑安裝,會在項目的node_modules中創建一個指向實際force-strict-loader目錄的軟鏈,也就是說之后我們可以隨時修改loader源碼並且不需要重復安裝了
- 修改webpack配置
module: { rules:[ { test: /\.js$/, use: 'force-strict-loader' } ] }
我們將這個loader設置為對所有js文件生效,此時對該工程進行打包,應該可以看到js文件的頭部都已經加上了啟用嚴格模式的語句。
2、啟用緩存
當文件輸入和其依賴沒有發生變化時,應該讓loader直接使用緩存。在webpack中可以使用this.cacheable進行控制,修改我們的loader。
//force-strict-laoder/index.js module.exports = function(content){ if(this.cacheable){ this.cacheable(); } var useStrictPrefix = '\'use strict\';\n\n'; return useStrictPrefix + content; }
通過啟用緩存可以加快webpack的打包速度,並且可保證相同的輸入產生相同的輸出。
3、獲取options
loader的配置項通過use.options傳進來,如:
{ test: /\.js$/, use: [ { loader: 'force-strict-loader', options: { sourceMap: true } }, ], }
下面我們在loader中獲取它
需要先安裝一個依賴庫 "loader-utils"
// force-strict-laoder/index.js
var loaderUtils = require('loader-utils'); module.exports = (content) => { if(this.cacheable){ this.cacheable(); } // 獲取和打印 options var options = loaderUtils.getOptions(this) || {} console.log('options', options); //處理content var useStrictPrefix = '\'use strict\'\n\n'; return useStrictPrefix + content; }
通過loaderUtils.getOptions可以獲取到配置對象,這里我們只是把它打印了出來。
4、實現Soure-Map
source-map可以便於實際開發者在瀏覽器控制台查看源碼。如果沒有對source-map進行處理,最終也就無法生成正確的map文件,在瀏覽器的dev tool中可能就會看到錯訊的源碼。
下面是支持了source-map特性后的版本:
var loaderUtils = require('loader-utils'); var SourceNode = require('source-map').SourceNode; var SourceMapConsumer = require('source-map').SourceMapConsumer; module.exports = (content, sourceMap) => { var useStrictPrefix = '\'use strict\'\n\n'; if(this.cacheable){ this.cacheable(); } // 獲取和打印 options var options = loaderUtils.getOptions(this) || {} console.log('options', options); // source-map if(options.sourceMap && sourceMap){ var currentRequest = loaderUtils.getCurrentRequest(this); var node = SourceNode.fromStringWithSourceMap(content, new SourceMapConsumer); } node.prepend(useStrictPrefix); var result = node.toStringWithSourceMap({ file: currentRequest }); var callback = this.async(); callback(null, result.code, result.map.toJSON()); //不支持source-map情況 return useStrictPrefix + content; }
首先,在loader函數的參數中我們獲取到sourceMap對象,這是由webpack或者上一個loader傳遞下來的,只有當它存在時我們的loader才能進行繼續處理和向下傳遞。
之后,我們通過source-map這個庫來對map進行操作,包括接收和消費之前的文件內容和source-map,對內容節點進行修改,最后產生新的source-map。
在函數返回的時候要使用this.async獲取callback函數(主要是為了一次性返回多個值)。callback函數的3個參數分別是拋出的錯誤、處理后的源碼、以后source-map。