Webpack 中怎么編寫loader,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
創(chuàng)新互聯(lián)建站專注于宿松網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供宿松營銷型網(wǎng)站建設(shè),宿松網(wǎng)站制作、宿松網(wǎng)頁設(shè)計、宿松網(wǎng)站官網(wǎng)定制、小程序設(shè)計服務(wù),打造宿松網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供宿松網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。
如果要做總結(jié)的話,我認(rèn)為 Loader 是一個帶有副作用的內(nèi)容轉(zhuǎn)譯器!
Webpack Loader 最核心的只能是實現(xiàn)內(nèi)容轉(zhuǎn)換器 —— 將各式各樣的資源轉(zhuǎn)化為標(biāo)準(zhǔn) JavaScript 內(nèi)容格式,例如:
css-loader 將 css 轉(zhuǎn)換為 __WEBPACK_DEFAULT_EXPORT__ = ".a{ xxx }"格式
html-loader 將 html 轉(zhuǎn)換為 __WEBPACK_DEFAULT_EXPORT__ = "
vue-loader 更復(fù)雜一些,會將 .vue 文件轉(zhuǎn)化為多個 JavaScript 函數(shù),分別對應(yīng) template、js、css、custom block
那么為什么需要做這種轉(zhuǎn)換呢?本質(zhì)上是因為 Webpack 只認(rèn)識符合 JavaScript 規(guī)范的文本(Webpack 5之后增加了其它 parser):在構(gòu)建(make)階段,解析模塊內(nèi)容時會調(diào)用 acorn 將文本轉(zhuǎn)換為 AST 對象,進(jìn)而分析代碼結(jié)構(gòu),分析模塊依賴;這一套邏輯對圖片、json、Vue SFC等場景就不 work 了,就需要 Loader 介入將資源轉(zhuǎn)化成 Webpack 可以理解的內(nèi)容形態(tài)。
Plugin 是 Webpack 另一套擴展機制,功能更強,能夠在各個對象的鉤子中插入特化處理邏輯,它可以覆蓋 Webpack 全生命流程,能力、靈活性、復(fù)雜度都會比 Loader 強很多,我們下次再講。
代碼層面,Loader 通常是一個函數(shù),結(jié)構(gòu)如下:
module.exports = function(source, sourceMap?, data?) { // source 為 loader 的輸入,可能是文件內(nèi)容,也可能是上一個 loader 處理結(jié)果 return source; };
Loader 函數(shù)接收三個參數(shù),分別為:
source:資源輸入,對于第一個執(zhí)行的 loader 為資源文件的內(nèi)容;后續(xù)執(zhí)行的 loader 則為前一個 loader 的執(zhí)行結(jié)果
sourceMap: 可選參數(shù),代碼的 sourcemap 結(jié)構(gòu)
data: 可選參數(shù),其它需要在 Loader 鏈中傳遞的信息,比如 posthtml/posthtml-loader 就會通過這個參數(shù)傳遞參數(shù)的 AST 對象
其中 source 是最重要的參數(shù),大多數(shù) Loader 要做的事情就是將 source 轉(zhuǎn)譯為另一種形式的 output ,比如 webpack-contrib/raw-loader 的核心源碼:
//... export default function rawLoader(source) { // ... const json = JSON.stringify(source) .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029'); const esModule = typeof options.esModule !== 'undefined' ? options.esModule : true; return `${esModule ? 'export default' : 'module.exports ='} ${json};`; }
這段代碼的作用是將文本內(nèi)容包裹成 JavaScript 模塊,例如:
// source I am Tecvan // output module.exports = "I am Tecvan"
經(jīng)過模塊化包裝之后,這段文本內(nèi)容轉(zhuǎn)身變成 Webpack 可以處理的資源模塊,其它 module 也就能引用、使用它了。
上例通過 return 語句返回處理結(jié)果,除此之外 Loader 還可以以 callback 方式返回更多信息,供下游 Loader 或者 Webpack 本身使用,例如在 webpack-contrib/eslint-loader 中:
export default function loader(content, map) { // ... linter.printOutput(linter.lint(content)); this.callback(null, content, map); }
通過 this.callback(null, content, map) 語句同時返回轉(zhuǎn)譯后的內(nèi)容與 sourcemap 內(nèi)容。callback 的完整簽名如下:
this.callback( // 異常信息,Loader 正常運行時傳遞 null 值即可 err: Error | null, // 轉(zhuǎn)譯結(jié)果 content: string | Buffer, // 源碼的 sourcemap 信息 sourceMap?: SourceMap, // 任意需要在 Loader 間傳遞的值 // 經(jīng)常用來傳遞 ast 對象,避免重復(fù)解析 data?: any );
及到異步或 CPU 密集操作時,Loader 中還可以以異步形式返回處理結(jié)果,例如 webpack-contrib/less-loader 的核心邏輯:
import less from "less"; async function lessLoader(source) { // 1. 獲取異步回調(diào)函數(shù) const callback = this.async(); // ... let result; try { // 2. 調(diào)用less 將模塊內(nèi)容轉(zhuǎn)譯為 css result = await (options.implementation || less).render(data, lessOptions); } catch (error) { // ... } const { css, imports } = result; // ... // 3. 轉(zhuǎn)譯結(jié)束,返回結(jié)果 callback(null, css, map); } export default lessLoader;
在 less-loader 中,邏輯分三步:
調(diào)用 this.async 獲取異步回調(diào)函數(shù),此時 Webpack 會將該 Loader 標(biāo)記為異步加載器,會掛起當(dāng)前執(zhí)行隊列直到 callback 被觸發(fā)
調(diào)用 less 庫將 less 資源轉(zhuǎn)譯為標(biāo)準(zhǔn) css
調(diào)用異步回調(diào) callback 返回處理結(jié)果
this.async 返回的異步回調(diào)函數(shù)簽名與上一節(jié)介紹的 this.callback 相同,此處不再贅述。
Loader 為開發(fā)者提供了一種便捷的擴展方法,但在 Loader 中執(zhí)行的各種資源內(nèi)容轉(zhuǎn)譯操作通常都是 CPU 密集型 —— 這放在單線程的 Node 場景下可能導(dǎo)致性能問題;又或者異步 Loader 會掛起后續(xù)的加載器隊列直到異步 Loader 觸發(fā)回調(diào),稍微不注意就可能導(dǎo)致整個加載器鏈條的執(zhí)行時間過長。
為此,默認(rèn)情況下 Webpack 會緩存 Loader 的執(zhí)行結(jié)果直到資源或資源依賴發(fā)生變化,開發(fā)者需要對此有個基本的理解,必要時可以通過 this.cachable 顯式聲明不作緩存,例如:
module.exports = function(source) { this.cacheable(false); // ... return output; };
除了作為內(nèi)容轉(zhuǎn)換器外,Loader 運行過程還可以通過一些上下文接口,有限制地影響 Webpack 編譯過程,從而產(chǎn)生內(nèi)容轉(zhuǎn)換之外的副作用。
上下文信息可通過 this 獲取,this 對象由 NormolModule.createLoaderContext 函數(shù)在調(diào)用 Loader 前創(chuàng)建,常用的接口包括:
const loaderContext = { // 獲取當(dāng)前 Loader 的配置信息 getOptions: schema => {}, // 添加警告 emitWarning: warning => {}, // 添加錯誤信息,注意這不會中斷 Webpack 運行 emitError: error => {}, // 解析資源文件的具體路徑 resolve(context, request, callback) {}, // 直接提交文件,提交的文件不會經(jīng)過后續(xù)的chunk、module處理,直接輸出到 fs emitFile: (name, content, sourceMap, assetInfo) => {}, // 添加額外的依賴文件 // watch 模式下,依賴文件發(fā)生變化時會觸發(fā)資源重新編譯 addDependency(dep) {}, };
其中,addDependency、emitFile 、emitError、emitWarning 都會對后續(xù)編譯流程產(chǎn)生副作用,例如 less-loader 中包含這樣一段代碼:
try { result = await (options.implementation || less).render(data, lessOptions); } catch (error) { // ... } const { css, imports } = result; imports.forEach((item) => { // ... this.addDependency(path.normalize(item)); });
解釋一下,代碼中首先調(diào)用 less 編譯文件內(nèi)容,之后遍歷所有 import 語句,也就是上例 result.imports 數(shù)組,一一調(diào)用 this.addDependency 函數(shù)將 import 到的其它資源都注冊為依賴,之后這些其它資源文件發(fā)生變化時都會觸發(fā)重新編譯。
使用上,可以為某種資源文件配置多個 Loader,Loader 之間按照配置的順序從前到后(pitch),再從后到前依次執(zhí)行,從而形成一套內(nèi)容轉(zhuǎn)譯工作流,例如對于下面的配置:
module.exports = { module: { rules: [ { test: /\.less$/i, use: [ "style-loader", "css-loader", "less-loader", ], }, ], }, };
這是一個典型的 less 處理場景,針對 .less 后綴的文件設(shè)定了:less、css、style 三個 loader 協(xié)作處理資源文件,按照定義的順序,Webpack 解析 less 文件內(nèi)容后先傳入 less-loader;less-loader 返回的結(jié)果再傳入 css-loader 處理;css-loader 的結(jié)果再傳入 style-loader;最終以 style-loader 的處理結(jié)果為準(zhǔn),流程簡化后如:
上述示例中,三個 Loader 分別起如下作用:
less-loader:實現(xiàn) less => css 的轉(zhuǎn)換,輸出 css 內(nèi)容,無法被直接應(yīng)用在 Webpack 體系下
css-loader:將 css 內(nèi)容包裝成類似 module.exports = "${css}" 的內(nèi)容,包裝后的內(nèi)容符合 JavaScript 語法
style-loader:做的事情非常簡單,就是將 css 模塊包進(jìn) require 語句,并在運行時調(diào)用 injectStyle 等函數(shù)將內(nèi)容注入到頁面的 style 標(biāo)簽
三個 Loader 分別完成內(nèi)容轉(zhuǎn)化工作的一部分,形成從右到左的調(diào)用鏈條。鏈?zhǔn)秸{(diào)用這種設(shè)計有兩個好處,一是保持單個 Loader 的單一職責(zé),一定程度上降低代碼的復(fù)雜度;二是細(xì)粒度的功能能夠被組裝成復(fù)雜而靈活的處理鏈條,提升單個 Loader 的可復(fù)用性。
不過,這只是鏈?zhǔn)秸{(diào)用的一部分,這里面有兩個問題:
Loader 鏈條一旦啟動之后,需要所有 Loader 都執(zhí)行完畢才會結(jié)束,沒有中斷的機會 —— 除非顯式拋出異常
某些場景下并不需要關(guān)心資源的具體內(nèi)容,但 Loader 需要在 source 內(nèi)容被讀取出來之后才會執(zhí)行
為了解決這兩個問題,Webpack 在 loader 基礎(chǔ)上疊加了 pitch 的概念。
網(wǎng)絡(luò)上關(guān)于 Loader 的文章已經(jīng)有非常非常多,但多數(shù)并沒有對 pitch 這一重要特性做足夠深入的介紹,沒有講清楚為什么要設(shè)計 pitch 這個功能,pitch 有哪些常見用例等。
在這一節(jié),我會從 what、how、why 三個維度展開聊聊 loader pitch 這一特性。
Webpack 允許在這個函數(shù)上掛載名為 pitch 的函數(shù),運行時 pitch 會比 Loader 本身更早執(zhí)行,例如:
const loader = function (source){ console.log('后執(zhí)行') return source; } loader.pitch = function(requestString) { console.log('先執(zhí)行') } module.exports = loader
Pitch 函數(shù)的完整簽名:
function pitch( remainingRequest: string, previousRequest: string, data = {} ): void { }
包含三個參數(shù):
remainingRequest : 當(dāng)前 loader 之后的資源請求字符串
previousRequest : 在執(zhí)行當(dāng)前 loader 之前經(jīng)歷過的 loader 列表
data : 與 Loader 函數(shù)的 data 相同,用于傳遞需要在 Loader 傳播的信息
這些參數(shù)不復(fù)雜,但與 requestString 緊密相關(guān),我們看個例子加深了解:
module.exports = { module: { rules: [ { test: /\.less$/i, use: [ "style-loader", "css-loader", "less-loader" ], }, ], }, };
css-loader.pitch 中拿到的參數(shù)依次為:
// css-loader 之后的 loader 列表及資源路徑 remainingRequest = less-loader!./xxx.less // css-loader 之前的 loader 列表 previousRequest = style-loader // 默認(rèn)值 data = {}
Pitch 翻譯成中文是拋、球場、力度、事物最高點等,我覺得 pitch 特性之所以被忽略完全是這個名字的鍋,它背后折射的是一整套 Loader 被執(zhí)行的生命周期概念。
實現(xiàn)上,Loader 鏈條執(zhí)行過程分三個階段:pitch、解析資源、執(zhí)行,設(shè)計上與 DOM 的事件模型非常相似,pitch 對應(yīng)到捕獲階段;執(zhí)行對應(yīng)到冒泡階段;而兩個階段之間 Webpack 會執(zhí)行資源內(nèi)容的讀取、解析操作,對應(yīng) DOM 事件模型的 AT_TARGET 階段:
pitch 階段按配置順序從左到右逐個執(zhí)行 loader.pitch 函數(shù)(如果有的話),開發(fā)者可以在 pitch 返回任意值中斷后續(xù)的鏈路的執(zhí)行:
那么為什么要設(shè)計 pitch 這一特性呢?在分析了 style-loader、vue-loader、to-string-loader 等開源項目之后,我個人總結(jié)出兩個字:「阻斷」!
先回顧一下前面提到過的 less 加載鏈條:
less-loader :將 less 規(guī)格的內(nèi)容轉(zhuǎn)換為標(biāo)準(zhǔn) css
css-loader :將 css 內(nèi)容包裹為 JavaScript 模塊
style-loader :將 JavaScript 模塊的導(dǎo)出結(jié)果以 link 、style 標(biāo)簽等方式掛載到 html 中,讓 css 代碼能夠正確運行在瀏覽器上
實際上, style-loader 只是負(fù)責(zé)讓 css 能夠在瀏覽器環(huán)境下跑起來,本質(zhì)上并不需要關(guān)心具體內(nèi)容,很適合用 pitch 來處理,核心代碼:
// ... // Loader 本身不作任何處理 const loaderApi = () => {}; // pitch 中根據(jù)參數(shù)拼接模塊代碼 loaderApi.pitch = function loader(remainingRequest) { //... switch (injectType) { case 'linkTag': { return `${ esModule ? `...` // 引入 runtime 模塊 : `var api = require(${loaderUtils.stringifyRequest( this, `!${path.join(__dirname, 'runtime/injectStylesIntoLinkTag.js')}` )}); // 引入 css 模塊 var content = require(${loaderUtils.stringifyRequest( this, `!!${remainingRequest}` )}); content = content.__esModule ? content.default : content;` } // ...`; } case 'lazyStyleTag': case 'lazySingletonStyleTag': { //... } case 'styleTag': case 'singletonStyleTag': default: { // ... } } }; export default loaderApi;
關(guān)鍵點:
loaderApi 為空函數(shù),不做任何處理
loaderApi.pitch 中拼接結(jié)果,導(dǎo)出的代碼包含:
引入運行時模塊 runtime/injectStylesIntoLinkTag.js復(fù)用 remainingRequest 參數(shù),重新引入 css 文件
運行結(jié)果大致如:
var api = require('xxx/style-loader/lib/runtime/injectStylesIntoLinkTag.js') var content = require('!!css-loader!less-loader!./xxx.less');
注意了,到這里 style-loader 的 pitch 函數(shù)返回這一段內(nèi)容,后續(xù)的 Loader 就不會繼續(xù)執(zhí)行,當(dāng)前調(diào)用鏈條中斷了:
之后,Webpack 繼續(xù)解析、構(gòu)建 style-loader 返回的結(jié)果,遇到 inline loader 語句:
var content = require('!!css-loader!less-loader!./xxx.less');
所以從 Webpack 的角度看,實際上對同一個文件調(diào)用了兩次 loader 鏈,第一次在 style-loader 的 pitch 中斷,第二次根據(jù) inline loader 的內(nèi)容跳過了 style-loader。
相似的技巧在其它倉庫也有出現(xiàn),比如 vue-loader,感興趣的同學(xué)可以查看我之前發(fā)在 ByteFE 公眾號上的文章《Webpack 案例 ——vue-loader 原理分析》,這里就不展開講了。
Webpack 為 Loader 開發(fā)者提供了兩個實用工具,在諸多開源 Loader 中出現(xiàn)頻率極高:
webpack/loader-utils:提供了一系列諸如讀取配置、requestString 序列化與反序列化、計算 hash 值之類的工具函數(shù)
webpack/schema-utils:參數(shù)校驗工具
這些工具的具體接口在相應(yīng)的 readme 上已經(jīng)有明確的說明,不贅述,這里總結(jié)一些編寫 Loader 時經(jīng)常用到的樣例:如何獲取并校驗用戶配置;如何拼接輸出文件名。
Loader 通常都提供了一些配置項,供開發(fā)者定制運行行為,用戶可以通過 Webpack 配置文件的 use.options 屬性設(shè)定配置,例如:
module.exports = { module: { rules: [{ test: /\.less$/i, use: [ { loader: "less-loader", options: { cacheDirectory: false } }, ], }], }, };
在 Loader 內(nèi)部,需要使用 loader-utils 庫的 getOptions 函數(shù)獲取用戶配置,用 schema-utils 庫的 validate 函數(shù)校驗參數(shù)合法性,例如 css-loader:
// css-loader/src/index.js import { getOptions } from "loader-utils"; import { validate } from "schema-utils"; import schema from "./options.json"; export default async function loader(content, map, meta) { const rawOptions = getOptions(this); validate(schema, rawOptions, { name: "CSS Loader", baseDataPath: "options", }); // ... }
使用 schema-utils 做校驗時需要提前聲明配置模板,通常會處理成一個額外的 json 文件,例如上例中的 "./options.json"。
Webpack 支持以類似 [path]/[name]-[hash].js 方式設(shè)定 output.filename即輸出文件的命名,這一層規(guī)則通常不需要關(guān)注,但某些場景例如 webpack-contrib/file-loader 需要根據(jù) asset 的文件名拼接結(jié)果。
file-loader 支持在 JS 模塊中引入諸如 png、jpg、svg 等文本或二進(jìn)制文件,并將文件寫出到輸出目錄,這里面有一個問題:假如文件叫 a.jpg ,經(jīng)過 Webpack 處理后輸出為 [hash].jpg ,怎么對應(yīng)上呢?此時就可以使用 loader-utils 提供的 interpolateName 在 file-loader 中獲取資源寫出的路徑及名稱,源碼:
import { getOptions, interpolateName } from 'loader-utils'; export default function loader(content) { const context = options.context || this.rootContext; const name = options.name || '[contenthash].[ext]'; // 拼接最終輸出的名稱 const url = interpolateName(this, name, { context, content, regExp: options.regExp, }); let outputPath = url; // ... let publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPath)}`; // ... if (typeof options.emitFile === 'undefined' || options.emitFile) { // ... // 提交、寫出文件 this.emitFile(outputPath, content, null, assetInfo); } // ... const esModule = typeof options.esModule !== 'undefined' ? options.esModule : true; // 返回模塊化內(nèi)容 return `${esModule ? 'export default' : 'module.exports ='} ${publicPath};`; } export const raw = true;
代碼的核心邏輯:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
根據(jù) Loader 配置,調(diào)用 interpolateName 方法拼接目標(biāo)文件的完整路徑
調(diào)用上下文 this.emitFile 接口,寫出文件
返回 module.exports = ${publicPath} ,其它模塊可以引用到該文件路徑
除 file-loader 外,css-loader、eslint-loader 都有用到該接口,感興趣的同學(xué)請自行前往查閱源碼。
在 Loader 中編寫單元測試收益非常高,一方面對開發(fā)者來說不用去怎么寫 demo,怎么搭建測試環(huán)境;一方面對于最終用戶來說,帶有一定測試覆蓋率的項目通常意味著更高、更穩(wěn)定的質(zhì)量。
閱讀了超過 20 個開源項目后,我總結(jié)了一套 Webpack Loader 場景下常用的單元測試流程,以 Jest · ? Delightful JavaScript Testing 為例:
鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
創(chuàng)建在 Webpack 實例,并運行 Loader
獲取 Loader 執(zhí)行結(jié)果,比對、分析判斷是否符合預(yù)期
判斷執(zhí)行過程中是否出錯
有兩種辦法,一是在 node 環(huán)境下運行調(diào)用 Webpack 接口,用代碼而非命令行執(zhí)行編譯,很多框架都會采用這種方式,例如 vue-loader、stylus-loader、babel-loader 等,優(yōu)點的運行效果最接近最終用戶,缺點是運行效率相對較低(可以忽略)。
以 posthtml/posthtml-loader 為例,它會在啟動測試之前創(chuàng)建并運行 Webpack 實例:
// posthtml-loader/test/helpers/compiler.js 文件 module.exports = function (fixture, config, options) { config = { /*...*/ } options = Object.assign({ output: false }, options) // 創(chuàng)建 Webpack 實例 const compiler = webpack(config) // 以 MemoryFS 方式輸出構(gòu)建結(jié)果,避免寫磁盤 if (!options.output) compiler.outputFileSystem = new MemoryFS() // 執(zhí)行,并以 promise 方式返回結(jié)果 return new Promise((resolve, reject) => compiler.run((err, stats) => { if (err) reject(err) // 異步返回執(zhí)行結(jié)果 resolve(stats) })) }
小技巧:如上例所示,用 compiler.outputFileSystem = new MemoryFS()語句將 Webpack 設(shè)定成輸出到內(nèi)存,能避免寫盤操作,提升編譯速度。
另外一種方法是編寫一系列 mock 方法,搭建起一個模擬的 Webpack 運行環(huán)境,例如 emaphp/underscore-template-loader ,優(yōu)點的運行速度更快,缺點是開發(fā)工作量大通用性低,了解了解即可。
上例運行結(jié)束之后會以 resolve(stats) 方式返回執(zhí)行結(jié)果,stats 對象中幾乎包含了編譯過程所有信息,包括耗時、產(chǎn)物、模塊、chunks、errors、warnings 等等,我在之前的文章 分享幾個 Webpack 實用分析工具 對此已經(jīng)做了較深入的介紹,感興趣的同學(xué)可以前往閱讀。
在測試場景下,可以從 stats 對象中讀取編譯最終輸出的產(chǎn)物,例如 style-loader 的實現(xiàn):
// style-loader/src/test/helpers/readAsset.js 文件 function readAsset(compiler, stats, assets) => { const usedFs = compiler.outputFileSystem const outputPath = stats.compilation.outputOptions.path const queryStringIdx = targetFile.indexOf('?') if (queryStringIdx >= 0) { // 解析出輸出文件路徑 asset = asset.substr(0, queryStringIdx) } // 讀文件內(nèi)容 return usedFs.readFileSync(path.join(outputPath, targetFile)).toString() }
解釋一下,這段代碼首先計算 asset 輸出的文件路徑,之后調(diào)用 outputFileSystem 的 readFile 方法讀取文件內(nèi)容。
接下來,有兩種分析內(nèi)容的方法:
調(diào)用 Jest 的 expect(xxx).toMatchSnapshot() 斷言判斷當(dāng)前運行結(jié)果是否與之前的運行結(jié)果一致,從而確保多次修改的結(jié)果一致性,很多框架都大量用了這種方法
解讀資源內(nèi)容,判斷是否符合預(yù)期,例如 less-loader 的單元測試中會對同一份代碼跑兩次 less 編譯,一次由 Webpack 執(zhí)行,一次直接調(diào)用 less 庫,之后分析兩次運行結(jié)果是否相同
對此有興趣的同學(xué),強烈建議看看 less-loader 的 test 目錄。
最后,還需要判斷編譯過程是否出現(xiàn)異常,同樣可以從 stats 對象解析:
export default getErrors = (stats) => { const errors = stats.compilation.errors.sort() return errors.map( e => e.toString() ) }
大多數(shù)情況下都希望編譯沒有錯誤,此時只要判斷結(jié)果數(shù)組是否為空即可。某些情況下可能需要判斷是否拋出特定異常,此時可以 expect(xxx).toMatchSnapshot() 斷言,用快照對比更新前后的結(jié)果。
開發(fā) Loader 的過程中,有一些小技巧能夠提升調(diào)試效率,包括:
使用 ndb 工具實現(xiàn)斷點調(diào)試
使用 npm link 將 Loader 模塊鏈接到測試項目
使用 resolveLoader 配置項將 Loader 所在的目錄加入到測試項目中,如:
// webpack.config.js module.exports = { resolveLoader:{ modules: ['node_modules','./loaders/'], } }
關(guān)于Webpack 中怎么編寫loader問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識。