從白盒到黑盒-開發(fā)自定義webpack插件記錄

之前很長一段時間,對webpack有一種畏懼的心理不铆,覺得里面配置復(fù)雜蝌焚,稍微寫錯某個地方就會導(dǎo)致本地服務(wù)跑不起來,樣式加載錯誤誓斥,less沒有被編譯只洒,ES6語法沒有被編譯。劳坑。都是一把辛酸淚毕谴。后來慢慢弄清entry,output距芬,然后慢慢加一些loader涝开,比如要開啟CSS module功能,比如要對某個資源文件編譯成JS框仔,學(xué)會用在webpack里面加上plugin舀武,讓項目的webpack功能更加強(qiáng)大。但是這一切都是停留在簡單使用上离斩,現(xiàn)在我終于嘗試去探究webpack內(nèi)部機(jī)制银舱,去利用webpack強(qiáng)大的可配置性做一些定制化的插件。

本文用詞比較通俗跛梗,如果表述過于小白寻馏,敬請見諒。實在是因為webpack整套流程和機(jī)制過于精妙核偿,我難窺一二诚欠。

怎么去了解和嘗試寫一個webpack插件

  1. 首先要寫一個工廠函數(shù),將這個函數(shù)的實例傳入webpack的plugins屬性
const Plugin = require('./plugin')
module.exports = {
    plugins: [
        new Plugin
    ]
}
  1. 這個工廠函數(shù)需要有apply方法漾岳,以下為es6寫法的該構(gòu)建函數(shù)
export default class DefPlugin {
    constructor(name) {}
    apply(compiler) {}
}
  1. 重點(diǎn)在于這個compiler對象了聂薪,它是webpack在構(gòu)建時傳入插件的實例對象

代表了webpack完整的構(gòu)建流程。該對象在啟動webpack時就被一次性創(chuàng)建蝗羊,由webpack組合所有的配置項(包括原始配置藏澳,加載器和插件)構(gòu)建生成。當(dāng)在webpack環(huán)境中應(yīng)用一個插件時耀找,插件會收到compiler的引用翔悠,通過使用compiler业崖,插件就可以訪問到整個webpack的環(huán)境(包括原始配置,加載器和插件)蓄愁。

然后它有很多事件鉤子双炕,類似生命周期,在構(gòu)建的某個生命周期會觸發(fā)對應(yīng)的事件鉤子撮抓,而我們寫插件無非就是對使用webpack打包的文件進(jìn)行自定義的處理妇斤,那我們要在合適的事件鉤子上注冊事件,在compiler對象上注冊事件的方法為plugin丹拯,當(dāng)然這是tapable0.2的寫法站超,對1.x寫法有興趣的可以查詢文檔,方法如下:

apply(compiler) {
    compiler.plugin('compilation', (compilation) => {
       // ...
    })
}
  1. 好乖酬,已經(jīng)知道compiler了死相,那么compilation又是個什么東西...

compilation--構(gòu)建過程:compilation對象在compiler的compile方法里創(chuàng)建,它代表了一次單一的版本構(gòu)建以及構(gòu)建生成資源的匯總咬像,compilation對象負(fù)責(zé)組織整個打包過程算撮,包含了每個構(gòu)建環(huán)節(jié)及輸出環(huán)節(jié)所對應(yīng)的方法。該對象內(nèi)部存放著所有module县昂、chunk肮柜、生成的assets以及用來生成最后打包文件的template的信息
當(dāng)運(yùn)行webpack-dev-server時,每當(dāng)檢測到一個文件變化倒彰,就會創(chuàng)建一次新的編譯素挽,從而生成一組新的編譯資源。

webpack專門提供了一個compilation鉤子狸驳,在這里可以獲取到compilation對象,我們可以通過compilation對象上的屬性做太多事情了缩赛,所有項目里的module在這里耙箍,chunk在這里...比如我要給改變我的某個module的值,我可以這樣做

// 講真這就是一個完整的自定義插件了酥馍,它可以把叫做test.js的module的內(nèi)容的world字段全局替換為lynn
apply(compiler) {
    compiler.plugin('compilation', (compilation) => {
        compilation.moduleTemplate.plugin('module', (source, module, options, dependencyTemplates) => {
            if (/test.js/.test(module.request)) {
                let newSource = source.source().replace(/world/g, 'lynn')
                return newSource
            } else {
                return source
            }
            return module
        })
    })
}
  1. 好吧...又出現(xiàn)新名詞ModuleTemplate辩昆,所有的module資源都是要經(jīng)過模板包裝一下輸出,compilation上有四個子類旨袒,子類上有相應(yīng)的方法汁针,可以監(jiān)聽事件來出來輸出的資源
    image.png

    比如我想改變某個module的代碼,可以這樣砚尽,每一個module都會觸發(fā)一次moduleTemplate的module事件施无,那么直接在這里做一個判斷,對想要改變的module進(jìn)行source改造(這里不顯示返回module和source是不行的)
    image.png

    我也可以在mainTemplate中改變整個bundle文件的源碼
    image.png

compiler事件鉤子

image.png

compilation事件鉤子

image.png

關(guān)于tapable

webpack 的插件架構(gòu)主要基于Tapable實現(xiàn)的必孤,Tapable 是 webpack 項目組的一個內(nèi)部庫猾骡,主要是抽象了一套插件機(jī)制瑞躺,而tapable的精妙之處在于可以玩轉(zhuǎn)任何插件。

  1. webpack 源代碼中的一些 Tapable 實例都繼承或混合了 Tapable 類兴想。Tapable 能夠讓我們?yōu)?javaScript 模塊添加并應(yīng)用插件幢哨。 它可以被其它模塊繼承或混合。
  2. 它類似于 NodeJS 的 EventEmitter 類嫂便,專注于自定義事件的觸發(fā)和操作捞镰。 除此之外, Tapable 允許你通過回調(diào)函數(shù)的參數(shù)訪問事件的生產(chǎn)者。
  3. webpack中很多對象實例都繼承了tapable類毙替,暴露了一個plugin方法岸售,可以用來監(jiān)聽某個事件
  4. 自定義插件需要有一個apply方法,因為插件實例傳入webpack后蔚龙,webpack會調(diào)用實例的apply方法冰评,傳入compiler對象,拿到這個對象就可以訪問webpack構(gòu)建過程中的信息了木羹,比如options甲雅,module應(yīng)有盡有,其中在合適的事件里可以獲取到compilation對象坑填,它代表了本次編譯過程抛人,其中又有很多事件鉤子供我們?nèi)燧d事件

敲黑板劃重點(diǎn)

如果沒有在文檔中有可能找不到合適的事件鉤子,那么這時可以去閱讀源碼找到鉤子

關(guān)于webpack的構(gòu)建流程 還要繼續(xù)學(xué)習(xí)

附上淘寶神圖一張webpack構(gòu)建流程圖和傳送門一個鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脐瑰,一起剝皮案震驚了整個濱河市妖枚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌苍在,老刑警劉巖绝页,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異寂恬,居然都是意外死亡续誉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門初肉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酷鸦,“玉大人,你說我怎么就攤上這事牙咏【矢簦” “怎么了?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵妄壶,是天一觀的道長摔握。 經(jīng)常有香客問我,道長丁寄,這世上最難降的妖魔是什么盒发? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任例嘱,我火速辦了婚禮,結(jié)果婚禮上宁舰,老公的妹妹穿的比我還像新娘拼卵。我一直安慰自己,他們只是感情好蛮艰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布腋腮。 她就那樣靜靜地躺著,像睡著了一般壤蚜。 火紅的嫁衣襯著肌膚如雪即寡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天袜刷,我揣著相機(jī)與錄音聪富,去河邊找鬼。 笑死著蟹,一個胖子當(dāng)著我的面吹牛墩蔓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播萧豆,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奸披,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涮雷?” 一聲冷哼從身側(cè)響起阵面,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎洪鸭,沒想到半個月后样刷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡览爵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年置鼻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拾枣。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盒让,靈堂內(nèi)的尸體忽然破棺而出梅肤,到底是詐尸還是另有隱情,我是刑警寧澤邑茄,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布姨蝴,位于F島的核電站,受9級特大地震影響肺缕,放射性物質(zhì)發(fā)生泄漏左医。R本人自食惡果不足惜授帕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望浮梢。 院中可真熱鬧跛十,春花似錦、人聲如沸秕硝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽远豺。三九已至奈偏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間躯护,已是汗流浹背惊来。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棺滞,地道東北人裁蚁。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像检眯,于是被迫代替她去往敵國和親厘擂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容