webpack源碼分析(一)— Tapable插件架構(gòu)

在前面的文章webpack不適合多頁(yè)面應(yīng)用?你寫的插件還不夠多中提到過(guò),webpack核心使用了Tapable實(shí)現(xiàn)事件的發(fā)布訂閱處理的插件架構(gòu)(Tapable中文文檔),今天就具體來(lái)分析下webpack基于Tapable的插件架構(gòu)

找到代碼入口

  • 想必你已經(jīng)使用過(guò)npm install webpack命令下載過(guò)webpack,那么在你的node_modules目錄下找到webpack。
  • npm模塊的入口文件可以通過(guò)package.json中的"main": "lib/webpack.js"找到,當(dāng)你通過(guò)reqire引用模塊的時(shí)候,其實(shí)定位到的就是這個(gè)文件。一般情況下,我們會(huì)在命令行直接使用webpack命令去執(zhí)行打包,這個(gè)時(shí)候執(zhí)行的就是bin/webpack.js了,這個(gè)命令只是在調(diào)用lib/webpack.js之前處理一些命令行參數(shù),殊途同歸。
  • 打開(kāi)lib/webpack.js。webpack.js除了使用exportPlugins導(dǎo)出很多插件類(方便外部調(diào)用),最重要的事情就是創(chuàng)建compiler對(duì)象(如果options是數(shù)組的話,每個(gè)元素創(chuàng)建一個(gè))
    //創(chuàng)建compiler對(duì)象
    compiler = new Compiler();
    //后面這幾句代碼,得看了compiler再回來(lái)看看了
    compiler.options = options;
    compiler.options = new WebpackOptionsApply().process(options, compiler);
    new NodeEnvironmentPlugin().apply(compiler);
    compiler.applyPlugins("environment");
    compiler.applyPlugins("after-environment");

這個(gè)時(shí)候我們的視線得轉(zhuǎn)移到compiler上了

植入Tapable

打開(kāi)lib/Compiler.js

    function Compiler() {

        //傳入作用域,調(diào)用Tapable的構(gòu)造函數(shù)
        Tapable.call(this);

        this.outputPath = "";
        this.outputFileSystem = null;
        this.inputFileSystem = null;

        this.recordsInputPath = null;
        this.recordsOutputPath = null;
        this.records = {};

        this.fileTimestamps = {};
        this.contextTimestamps = {};

        this.resolvers = {
            normal: new Resolver(null),
            loader: new Resolver(null),
            context: new Resolver(null)
        };
        this.parser = new Parser();

        this.options = {};
    }
    module.exports = Compiler;

    //復(fù)制一份Tapable的原型
    Compiler.prototype = Object.create(Tapable.prototype);
    Compiler.prototype.constructor = Compiler;

如果你閱讀過(guò)Tapable中文文檔,你應(yīng)該對(duì)這個(gè)mix的方式不會(huì)陌生,tapable的原理其實(shí)也不復(fù)雜

聲明一個(gè)全局的變量this._plugins = {},插件中使用plugin(name, fn)方法給事件name注冊(cè)處理方法fn,多次注冊(cè)形成了事件name的監(jiān)聽(tīng)鏈,當(dāng)事件name觸發(fā)的時(shí)候,執(zhí)行這些處理方法。處理方法的執(zhí)行順序和執(zhí)行方式依據(jù)事件name的觸發(fā)方式的不同而不同

這個(gè)時(shí)候compiler已經(jīng)具備Tapable的所有屬性和方法了,我們?cè)倩氐絣ib/webpack.js來(lái)看看創(chuàng)建了Compiler對(duì)象后的幾行代碼

說(shuō)實(shí)話不太欣賞這種在對(duì)象外面初始化的設(shè)計(jì)模式,讀代碼的時(shí)候你得跳來(lái)跳去,我更傾向于通過(guò)構(gòu)造函數(shù)傳入options,在對(duì)象內(nèi)進(jìn)行初始化工作。(僅代表個(gè)人的想法)

    //給對(duì)象參數(shù)賦值
    compiler.options = options;
    //傳入options和compiler執(zhí)行WebpackOptionsApply的process想法
    //這個(gè)方法對(duì)參數(shù)進(jìn)行了處理,并且注入了大量的插件
    compiler.options = new WebpackOptionsApply().process(options, compiler);
    //注冊(cè)nodeEveironmentPlugin插件
    new NodeEnvironmentPlugin().apply(compiler);
    //觸發(fā)environment和after-environment事件
    compiler.applyPlugins("environment");
    compiler.applyPlugins("after-environment");

webpack很多核心功能本身就是以插件的形式開(kāi)發(fā)的,打開(kāi)lib/WebpackOptionsApply就會(huì)發(fā)現(xiàn),在這個(gè)方法中,除了處理參數(shù),就是把一個(gè)個(gè)插件注冊(cè)到compiler中

compiler.applyPlugins("environment")以一種最簡(jiǎn)單的并行處理的方式去去觸發(fā)事件environment事件,所有注冊(cè)的處理方法并行執(zhí)行,相互獨(dú)立互不干擾,并且不需要給處理方法傳入?yún)?shù)。

而有些事件的觸發(fā)方式要復(fù)雜一些,例如complier觸發(fā)emit的方式

    this.applyPluginsAsync("emit", compilation, function(err) {
        if(err) return callback(err);
        outputPath = compilation.getPath(this.outputPath);
        this.outputFileSystem.mkdirp(outputPath, emitFiles.bind(this));
    }.bind(this));

這段代碼:
觸發(fā)事件emit,傳入?yún)?shù)compilation對(duì)象,串行的調(diào)用注冊(cè)在事件emit上的處理函數(shù)(先入先出),倘若某一個(gè)處理函數(shù)報(bào)錯(cuò),則執(zhí)行傳入的function(err),后續(xù)的處理函數(shù)將不被執(zhí)行,否則最后一個(gè)處理函數(shù)調(diào)用function()。插件注冊(cè)此類事件,處理函數(shù)需要調(diào)用callback,這樣才能保證監(jiān)聽(tīng)鏈的正確執(zhí)行。所以為了在寫自定義插件的時(shí)候能正確的監(jiān)聽(tīng)事件,非常有必要仔細(xì)讀Tapable中文文檔(雖然本文已經(jīng)多次提到這個(gè)文檔,但是還是有必要再進(jìn)行一次提醒)

webpack中另外一個(gè)重要的對(duì)象compilation使用了同樣的方式植入了tapable

總結(jié)

咱們分析webpack源碼主要有兩個(gè)原因:一是為了學(xué)習(xí)優(yōu)秀的代碼的設(shè)計(jì)方法,二是為了編寫webpack自定義插件的時(shí)候能夠游刃有余。不管從哪一點(diǎn)來(lái)說(shuō),Tapable面向切面的插件思想,都是值得我們琢磨的(在我的一個(gè)項(xiàng)目中還真的從webpack把Tapable借鑒過(guò)來(lái)了)

后續(xù)會(huì)繼續(xù)更新對(duì)webpack源碼的進(jìn)一步分析,歡迎關(guān)注,共同學(xué)習(xí)。有問(wèn)題請(qǐng)?jiān)u論或發(fā)簡(jiǎn)信,如果你覺(jué)得文章對(duì)你有所幫助,請(qǐng)不要吝惜你的喜歡,當(dāng)然,給我打賞我也不會(huì)客氣的~~。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • GitChat技術(shù)雜談 前言 本文較長(zhǎng),為了節(jié)省你的閱讀時(shí)間,在文前列寫作思路如下: 什么是 webpack,它要...
    蕭玄辭閱讀 12,891評(píng)論 7 110
  • 說(shuō)在前面:這些文章均是本人花費(fèi)大量精力研究整理,如有轉(zhuǎn)載請(qǐng)聯(lián)系作者并注明引用,謝謝本文的受眾人群不是webpack...
    RockSAMA閱讀 7,081評(píng)論 2 7
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,554評(píng)論 19 139
  • 無(wú)意中看到zhangwnag大佬分享的webpack教程感覺(jué)受益匪淺,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,368評(píng)論 7 35
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,273評(píng)論 6 342

友情鏈接更多精彩內(nèi)容