為什么需要loader?
Webpack 本身能處理 js 和 JSON 文件硫嘶,如果要處理其他類型的模塊锰悼,就需要使用loader來對模塊進行轉換勒极。
loader是什么饲帅?
loader本質上是一個node模塊敞恋,符合Webpack中一切皆模塊的思想丽啡。
loader本身就是一個函數,在該函數中對接收到的內容進行轉換硬猫,然后返回轉換后的結果补箍。
loader的運行機制
loader 是 Webpack 的核心概念之一,它的基本工作流是將一個模塊以字符串的形式讀入啸蜜,對其進行語法分析及轉換坑雅,然后交由下一個環(huán)節(jié)進行處理,所有載入的模塊最終都會經過 moduleFactory 處理衬横,轉成 JavaScript 可以識別和運行的代碼裹粤,從而完成模塊的集成。
loader的特點:
- 鏈式調用:第一個loader接收到的是源文件的內容冕香,后續(xù)loader接收到的都是上一個loader 返回的處理結果蛹尝,webpack 會按順序鏈式調用每個 loader;
- 單一職責:每個loader 只做一件事悉尾;
- 統(tǒng)一原則:遵循 Webpack 制定的設計規(guī)則和結構突那,輸入與輸出均為字符串,各個 Loader 完全獨立构眯,即插即用愕难;
- 模塊化:保證 loader 是模塊化的。loader 生成模塊需要遵循和普通模塊一樣的設計原則;
- 無狀態(tài):在多次模塊的轉化之間猫缭,我們不應該在 loader 中保留狀態(tài)葱弟。每個 loader 運行時應該確保與其他編譯好的模塊保持獨立,同樣也應該與前幾個 loader 對相同模塊的編譯結果保持獨立猜丹。
loader的執(zhí)行順序:點擊查看文章芝加、Pitching Loader
- 正常loader的執(zhí)行順序是從右往左(從下往上)。但是loader在處理資源文件時分兩個階段射窒,Pitch階段和Normal階段藏杖;
- loader本身就是一個方法,pitch是loader這個方法的一個屬性脉顿,pitch也是一個方法蝌麸;
- Pitch階段:這是loader處理資源文件之前的階段。在這個階段艾疟,loader的pitch方法會按照
后置(post)来吩、行內(inline)、普通(normal)蔽莱、前置(pre)
的順序被調用弟疆。- Normal階段:這是loader對源文件進行轉譯的階段。在這個階段碾褂,loader的常規(guī)方法會按照
前置(pre)兽间、普通(normal)、行內(inline)正塌、后置(post)
的順序被調用。模塊源碼的轉換恤溶, 就發(fā)生在這個階段乓诽。- 如果loader都有Pitch階段和Normal階段,并且Pitch都沒有返回值的話咒程,那么先從左到右(從上到下)執(zhí)行Pitch鸠天,然后再從右到左(從下到上)執(zhí)行l(wèi)oader的源碼轉換。
- 如果loader都有Pitch階段和Normal階段帐姻,然后某個Pitch有返回值的話稠集,那么當前Pitch的loader和后面的loader、pitch都不會再執(zhí)行了饥瓷。例如:loader1(pitch1)剥纷、loader2(pitch2,pitch2有return返回值)呢铆、loader3(pitch3)晦鞋,此時的執(zhí)行順序是picth1 => pitch2 => loader1
loader的返回值:
loader的原理就是將輸入的源內容進行處理后返回,loader 有兩種方式返回處理后的內容:1. return source:
module.exports = function (source, sourceMaps) {
// 在這里按照你的需求處理 source,source是需要處理的內容字符串
// sourceMaps可以用來調試
return source.replace('world', 'Ltraveller')
}
2. this.callback():
this.callback()除了可以返回處理過的內容以外悠垛,還可以返回其它信息线定,this.callback() 是 webpack 給 loader 注入的 API,方便 loader 和 webpack之間通信确买。
module.exports = function(source, sourceMaps) {
// 通過 this.callback 告訴 Webpack 返回的結果斤讥,還返回了sourceMaps
this.callback(null, source.replace('world', 'Ltraveller'), sourceMaps);
return;
};
this.callback() 的詳細用法如下:
this.callback(
// 當無法轉換原內容時,給 Webpack 返回一個 Error
err: Error | null,
// 原內容經過轉換處理后的新內容
content: string | Buffer,
// 用于把轉換后的內容得出原內容的 Source Map湾趾,方便調試
sourceMap?: SourceMap,
// 如果本次轉換為原內容生成了 AST 語法樹周偎,可以把這個 AST 返回,以方便之后需要 AST 的 Loader 復用該 AST,以避免重復生成 AST撑帖,提升性能
abstractSyntaxTree?: AST
);
同步與異步 loader
- 同步loader需要return蓉坎,即使使用this.callback(),也需要return undefined胡嘿,以上都是同步loader蛉艾;
- 異步loader 使用 const callback = this.async(),this.async() 將這個 loader 變成是一個異步 loader衷敌。
module.exports = function (source) {
// 調用 this.async() API勿侯,告訴 webpack本次轉換是異步的,loader 會在 callback 中返回結果
const callback = this.async();
// 使用 setTimeout 模擬異步過程
setTimeout(() => {
const content = source.replace('world', 'Ltraveller');
// 通過 callback 返回執(zhí)行異步后的結果
callback(null, content);
}, 3000);
};
Webpack 提供了loader-utils工具可以去獲取loader的options配置缴罗。
const loaderUtils = require('loader-utils')
module.exports = function (source) {
// 獲取到用戶給當前 Loader 傳入的 options
const options = loaderUtils.getOptions(this) // 獲取配置的options
console.log('options-->', options)
// 在這里按照你的需求處理 source
return source.replace('world', 'Ltraveller')
}
配置
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: path.resolve(__dirname, "./myLoaders/my-loader")
options: {
flag: true,
},
},
],
},
],
},