先說(shuō)下webpack打包原理
- 識(shí)別入口文件
- 通過(guò)逐層識(shí)別模塊依賴(lài)。(Commonjs、amd或者es6的import摹量,webpack都會(huì)對(duì)其進(jìn)行分析。來(lái)獲取代碼的依賴(lài))
- webpack做的就是分析代碼馒胆。轉(zhuǎn)換代碼缨称,編譯代碼,輸出代碼
最終形成打包后的代碼
這些都是webpack的一些基礎(chǔ)知識(shí)祝迂,對(duì)于理解webpack的工作機(jī)制很有幫助睦尽。
步入正題,loader
什么是lodaer呢型雳,其實(shí)就是一個(gè)個(gè)函數(shù)当凡,loader是文件加載器山害,能夠加載資源文件,并對(duì)這些文件進(jìn)行一些處理宁玫,諸如編譯粗恢、壓縮等,最終一起打包到指定的文件中
- loader是文件加載器欧瘪,能夠加載資源文件眷射,并對(duì)這些文件進(jìn)行一些處理,諸如編譯佛掖、壓縮等妖碉,最終一起打包到指定的文件中
- 第一個(gè)執(zhí)行的loader接收源文件內(nèi)容作為參數(shù),其他loader接收前一個(gè)執(zhí)行的loader的返回值作為參數(shù)芥被。最后執(zhí)行的loader會(huì)返回此模塊的JavaScript源碼
手寫(xiě)一個(gè)loader
需求:
1.處理.txt文件
2.對(duì)字符串做反轉(zhuǎn)操作
3.首字母大寫(xiě)
4.對(duì)打印出來(lái)的111進(jìn)行重寫(xiě)
例如:abcdefg轉(zhuǎn)換后為Gfedcba, console.log(111)=>console.log(222)
let's go!~
-
首先創(chuàng)建兩個(gè)loader(這里以本地loader為例)
為什么要?jiǎng)?chuàng)建兩個(gè)laoder欧宜?理由后面會(huì)介紹
看,loader結(jié)構(gòu)是不是很簡(jiǎn)單拴魄,接收一個(gè)參數(shù)冗茸,并且return一個(gè)內(nèi)容就ok了。
然后創(chuàng)建一個(gè)txt文件,里面隨便寫(xiě)點(diǎn)什么匹中,abcdef都行
現(xiàn)在開(kāi)始配置webpack,
我們?cè)谌肟谖募袑?dǎo)入這個(gè)腳本
為什么這里需要導(dǎo)入呢夏漱,我們不是配置了webapck處理所有的.txt文件么?
因?yàn)閣ebpack會(huì)做過(guò)濾顶捷,如果不引用該文件的話挂绰,webpack是不會(huì)對(duì)該文件進(jìn)行打包處理的,那么你的loader也不會(huì)執(zhí)行(好像有點(diǎn)廢話- - 服赎!)
這個(gè)就是入口文件啦葵蒂,我稍微做了點(diǎn)操作,目的是為了展示在頁(yè)面中重虑。
那你可能有疑問(wèn)了践付,html怎么才能夠展示呢,很簡(jiǎn)單缺厉,把打包之后dist中的js引入html就OK
然后允許webpack就好了永高。webpack會(huì)自動(dòng)去找webpack.config.js。
現(xiàn)在回答為什么要寫(xiě)兩個(gè)loader芽死?
看到執(zhí)行的順序沒(méi)乏梁,我們的配置的是這樣的次洼。
use: [
'./loader/uppercase-loader.js',
'./loader/reverse-loader.js'
]
正如前文所說(shuō)关贵, 處理一個(gè)文件可以使用多個(gè)loader,loader的執(zhí)行順序是和本身的順序是相反的卖毁。
我們也可以自己寫(xiě)loader解析自定義模板揖曾,像vue-loader是非常復(fù)雜的落萎,它內(nèi)部會(huì)寫(xiě)大量的對(duì).vue文件的解析,然后會(huì)生成對(duì)應(yīng)的html炭剪、js和css练链。
我們這里只是講述了一個(gè)最基礎(chǔ)的用法,如果有更多的需要奴拦,可以查看《loader官方文檔》
了解了基本模式后媒鼓,我們先不急著開(kāi)發(fā)。所謂磨刀不誤砍柴工错妖,我們先看看開(kāi)發(fā)一個(gè) loader 需要注意些什么绿鸣,這樣可以少走彎路,提高開(kāi)發(fā)質(zhì)量暂氯。下面是 webpack 提供的幾點(diǎn)指南潮模,它們按重要程度排序,注意其中有些點(diǎn)只適用特定情況痴施。
獲得 Loader 的 options
const loaderUtils = require('loader-utils');
module.exports = function(source) {
// 獲取到用戶(hù)給當(dāng)前 Loader 傳入的 options
const options = loaderUtils.getOptions(this);
return source;
};
返回其它結(jié)果
上面的 Loader 都只是返回了原內(nèi)容轉(zhuǎn)換后的內(nèi)容擎厢,但有些場(chǎng)景下還需要返回除了內(nèi)容之外的東西。
例如以用 babel-loader 轉(zhuǎn)換 ES6 代碼為例辣吃,它還需要輸出轉(zhuǎn)換后的 ES5 代碼對(duì)應(yīng)的 Source Map动遭,以方便調(diào)試源碼。
為了把 Source Map 也一起隨著 ES5 代碼返回給 Webpack齿尽,可以這樣寫(xiě):
module.exports = function(source) {
// 通過(guò) this.callback 告訴 Webpack 返回的結(jié)果
this.callback(null, source, sourceMaps);
// 當(dāng)你使用 this.callback 返回內(nèi)容時(shí)沽损,該 Loader 必須返回 undefined,
// 以讓 Webpack 知道該 Loader 返回的結(jié)果在 this.callback 中循头,而不是 return 中
return;
};
其中的 this.callback 是 Webpack 給 Loader 注入的 API绵估,以方便 Loader 和 Webpack 之間通信。
this.callback 的詳細(xì)使用方法如下:
this.callback(
// 當(dāng)無(wú)法轉(zhuǎn)換原內(nèi)容時(shí)卡骂,給 Webpack 返回一個(gè) Error
err: Error | null,
// 原內(nèi)容轉(zhuǎn)換后的內(nèi)容
content: string | Buffer,
// 用于把轉(zhuǎn)換后的內(nèi)容得出原內(nèi)容的 Source Map国裳,方便調(diào)試
sourceMap?: SourceMap,
// 如果本次轉(zhuǎn)換為原內(nèi)容生成了 AST 語(yǔ)法樹(shù),可以把這個(gè) AST 返回全跨,
// 以方便之后需要 AST 的 Loader 復(fù)用該 AST缝左,以避免重復(fù)生成 AST,提升性能
abstractSyntaxTree?: AST
);
Source Map 的生成很耗時(shí)浓若,通常在開(kāi)發(fā)環(huán)境下才會(huì)生成 Source Map渺杉,其它環(huán)境下不用生成,以加速構(gòu)建挪钓。
為此 Webpack 為 Loader 提供了 this.sourceMap API 去告訴 Loader 當(dāng)前構(gòu)建環(huán)境下用戶(hù)是否需要 Source Map是越。
如果你編寫(xiě)的 Loader 會(huì)生成 Source Map,請(qǐng)考慮到這點(diǎn)碌上。
同步與異步
Loader 有同步和異步之分倚评,上面介紹的 Loader 都是同步的 Loader浦徊,因?yàn)樗鼈兊霓D(zhuǎn)換流程都是同步的,轉(zhuǎn)換完成后再返回結(jié)果天梧。
但在有些場(chǎng)景下轉(zhuǎn)換的步驟只能是異步完成的盔性,例如你需要通過(guò)網(wǎng)絡(luò)請(qǐng)求才能得出結(jié)果,如果采用同步的方式網(wǎng)絡(luò)請(qǐng)求就會(huì)阻塞整個(gè)構(gòu)建呢岗,導(dǎo)致構(gòu)建非常緩慢冕香。
在轉(zhuǎn)換步驟是異步時(shí),你可以這樣:
module.exports = function(source) {
// 告訴 Webpack 本次轉(zhuǎn)換是異步的后豫,Loader 會(huì)在 callback 中回調(diào)結(jié)果
var callback = this.async();
someAsyncOperation(source, function(err, result, sourceMaps, ast) {
// 通過(guò) callback 返回異步執(zhí)行后的結(jié)果
callback(err, result, sourceMaps, ast);
});
};
處理二進(jìn)制數(shù)據(jù)
在默認(rèn)的情況下暂筝,Webpack 傳給 Loader 的原內(nèi)容都是 UTF-8 格式編碼的字符串。
但有些場(chǎng)景下 Loader 不是處理文本文件硬贯,而是處理二進(jìn)制文件焕襟,例如 file-loader,就需要 Webpack 給 Loader 傳入二進(jìn)制格式的數(shù)據(jù)饭豹。
為此鸵赖,你需要這樣編寫(xiě) Loader:
module.exports = function(source) {
// 在 exports.raw === true 時(shí),Webpack 傳給 Loader 的 source 是 Buffer 類(lèi)型的
source instanceof Buffer === true;
// Loader 返回的類(lèi)型也可以是 Buffer 類(lèi)型的
// 在 exports.raw !== true 時(shí)拄衰,Loader 也可以返回 Buffer 類(lèi)型的結(jié)果
return source;
};
// 通過(guò) exports.raw 屬性告訴 Webpack 該 Loader 是否需要二進(jìn)制數(shù)據(jù)
module.exports.raw = true;
以上代碼中最關(guān)鍵的代碼是最后一行 module.exports.raw = true;它褪,沒(méi)有該行 Loader 只能拿到字符串。
緩存加速
在有些情況下翘悉,有些轉(zhuǎn)換操作需要大量計(jì)算非常耗時(shí)茫打,如果每次構(gòu)建都重新執(zhí)行重復(fù)的轉(zhuǎn)換操作,構(gòu)建將會(huì)變得非常緩慢妖混。
為此老赤,Webpack 會(huì)默認(rèn)緩存所有 Loader 的處理結(jié)果,也就是說(shuō)在需要被處理的文件或者其依賴(lài)的文件沒(méi)有發(fā)生變化時(shí)制市,
是不會(huì)重新調(diào)用對(duì)應(yīng)的 Loader 去執(zhí)行轉(zhuǎn)換操作的抬旺。
如果你想讓 Webpack 不緩存該 Loader 的處理結(jié)果,可以這樣:
module.exports = function(source) {
// 關(guān)閉該 Loader 的緩存功能
this.cacheable(false);
return source;
};
還有一些loader API就不一一說(shuō)了祥楣,想了解的同學(xué)开财,上面都有鏈接的,直接去官網(wǎng)看就好了误褪。
下面附上git地址:
git源碼地址
希望對(duì)你有所幫助哦~
最后手寫(xiě)不易责鳍,點(diǎn)個(gè)贊吧??