成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

webpack源碼之運行流程

kviccn / 2275人閱讀

摘要:引言通過前面幾張的鋪墊下面開始分析源碼核心流程大體上可以分為初始化編譯輸出三個階段下面開始分析初始化這個階段整體流程做了什么啟動構(gòu)建,讀取與合并配置參數(shù),加載,實例化。推薦源碼之源碼之機制源碼之簡介源碼之機制參考源碼

引言

通過前面幾張的鋪墊,下面開始分析webpack源碼核心流程,大體上可以分為初始化,編譯,輸出三個階段,下面開始分析

初始化
這個階段整體流程做了什么? 啟動構(gòu)建,讀取與合并配置參數(shù),加載 Plugin,實例化 Compiler。
詳細分析
//通過yargs獲得shell中的參數(shù)
yargs.parse(process.argv.slice(2), (err, argv, output) => {
    //把webpack.config.js中的參數(shù)和shell參數(shù)整合到options對象上
    let options;
        options = require("./convert-argv")(argv);

    function processOptions(options) {

        const firstOptions = [].concat(options)[0];
        const webpack = require("webpack");

        let compiler;
            //通過webpack方法創(chuàng)建compile對象,Compiler 負責(zé)文件監(jiān)聽和啟動編譯。
            //Compiler 實例中包含了完整的 Webpack 配置,全局只有一個 Compiler 實例。
            compiler = webpack(options);


        if (firstOptions.watch || options.watch) {

            compiler.watch(watchOptions, compilerCallback);
            //啟動一次新的編譯。
        } else compiler.run(compilerCallback);
    }

    processOptions(options);
});

說明 從源碼中摘取了初始化的的第一步,做了簡化,當(dāng)運行webpack命令的的時候,運行的是webpack-cli下webpack.js,其內(nèi)容是一個自執(zhí)行函數(shù),上面是執(zhí)行的第一步,進行參數(shù)的解析合并處理,并創(chuàng)建compiler實例,然后啟動編譯運行run方法,其中關(guān)鍵步驟 compiler = webpack(options); 詳細展開如下所示

const webpack = (options, callback) => {
    //參數(shù)合法性校驗
    const webpackOptionsValidationErrors = validateSchema(
        webpackOptionsSchema,
        options
    );

    let compiler;
    if (Array.isArray(options)) {
        compiler = new MultiCompiler(options.map(options => webpack(options)));
    } else if (typeof options === "object") {
        options = new WebpackOptionsDefaulter().process(options);
        //創(chuàng)建compiler對象
        compiler = new Compiler(options.context);
        compiler.options = options;
        new NodeEnvironmentPlugin().apply(compiler);
        //注冊配置文件中的插件,依次調(diào)用插件的 apply 方法,讓插件可以監(jiān)聽后續(xù)的所有事件節(jié)點。同時給插件傳入 compiler 實例的引用,以方便插件通過 compiler 調(diào)用 Webpack 提供的 API。
        if (options.plugins && Array.isArray(options.plugins)) {
            for (const plugin of options.plugins) {
                plugin.apply(compiler);
            }
        }
        //開始應(yīng)用 Node.js 風(fēng)格的文件系統(tǒng)到 compiler 對象,以方便后續(xù)的文件尋找和讀取。
        compiler.hooks.environment.call();
        compiler.hooks.afterEnvironment.call();
        //注冊內(nèi)部插件
        compiler.options = new WebpackOptionsApply().process(options, compiler);
    }

    return compiler;
};

說明 注冊插件過程不在展開,webpack內(nèi)置插件真的很多啊

編譯
這個階段整體流程做了什么? 從 Entry 發(fā)出,針對每個 Module 串行調(diào)用對應(yīng)的 Loader 去翻譯文件內(nèi)容,再找到該 Module 依賴的 Module,遞歸地進行編譯處理。
詳細分析
this.hooks.beforeRun.callAsync(this, err => {
            if (err) return finalCallback(err);

            this.hooks.run.callAsync(this, err => {
                if (err) return finalCallback(err);

                this.readRecords(err => {
                    if (err) return finalCallback(err);

                    this.compile(onCompiled);
                });
            });
        });

說明 從執(zhí)行run方法開始,開始執(zhí)行編譯流程,run方法觸發(fā)了before-run、run兩個事件,然后通過readRecords讀取文件,通過compile進行打包,該方法中實例化了一個Compilation類

compile(callback) {
        const params = this.newCompilationParams();
        this.hooks.beforeCompile.callAsync(params, err => {
            if (err) return callback(err);

            this.hooks.compile.call(params);
// 每編譯一次都會創(chuàng)建一個compilation對象(比如watch 文件時,一改動就會執(zhí)行),但是compile只會創(chuàng)建一次
            const compilation = this.newCompilation(params);
// make事件觸發(fā)了  事件會觸發(fā)SingleEntryPlugin監(jiān)聽函數(shù),調(diào)用compilation.addEntry方法
            this.hooks.make.callAsync(compilation, err => {
                if (err) return callback(err);
                
            });
        });
    }

說明 打包時觸發(fā)before-compile、compile、make等事件,同時創(chuàng)建非常重要的compilation對象,內(nèi)部有聲明了很多鉤子,初始化模板等等

this.hooks = {
    buildModule: new SyncHook(["module"]),
    seal: new SyncHook([]),
    optimize: new SyncHook([]),
};
//拼接最終生成代碼的主模板會用到
this.mainTemplate = new MainTemplate(this.outputOptions);
//拼接最終生成代碼的chunk模板會用到
this.chunkTemplate = new ChunkTemplate(this.outputOptions); 
 //拼接最終生成代碼的熱更新模板會用到
this.hotUpdateChunkTemplate = new HotUpdateChunkTemplate()
//監(jiān)聽comple的make hooks事件,通過內(nèi)部的 SingleEntryPlugin 從入口文件開始執(zhí)行編譯
        compiler.hooks.make.tapAsync(
            "SingleEntryPlugin",
            (compilation, callback) => {
                const { entry, name, context } = this;

                const dep = SingleEntryPlugin.createDependency(entry, name);
                compilation.addEntry(context, dep, name, callback);
            }
        );

說明 監(jiān)聽compile的make hooks事件,通過內(nèi)部的 SingleEntryPlugin 從入口文件開始執(zhí)行編譯,調(diào)用compilation.addEntry方法,根據(jù)模塊的類型獲取對應(yīng)的模塊工廠并創(chuàng)建模塊,開始構(gòu)建模塊

doBuild(options, compilation, resolver, fs, callback) {
    const loaderContext = this.createLoaderContext(
        resolver,
        options,
        compilation,
        fs
    );
    //調(diào)用loader處理模塊
    runLoaders(
        {
            resource: this.resource,
            loaders: this.loaders,
            context: loaderContext,
            readResource: fs.readFile.bind(fs)
        },
        (err, result) => {
           
            
            const resourceBuffer = result.resourceBuffer;
            const source = result.result[0];
            const sourceMap = result.result.length >= 1 ? result.result[1] : null;
            const extraInfo = result.result.length >= 2 ? result.result[2] : null;
            

            this._source = this.createSource(
                this.binary ? asBuffer(source) : asString(source),
                resourceBuffer,
                sourceMap
            );
            //loader處理完之后 得到_source  然后ast接著處理
            this._ast =
                typeof extraInfo === "object" &&
                extraInfo !== null &&
                extraInfo.webpackAST !== undefined
                    ? extraInfo.webpackAST
                    : null;
            return callback();
        }
    );
}

說明 SingleEntryPlugin這個內(nèi)存插件主要作用是從entry讀取文件,根據(jù)文件類型和配置的 Loader 執(zhí)行runLoaders,然后將loader處理后的文件通過acorn抽象成抽象語法樹AST,遍歷AST,構(gòu)建該模塊的所有依賴。

輸出
這個階段整體流程做了什么? 把編譯后的 Module 組合成 Chunk,把 Chunk 轉(zhuǎn)換成文件,輸出到文件系統(tǒng)。
詳細分析
 //所有依賴build完成,開始對chunk進行優(yōu)化(抽取公共模塊、加hash等)
compilation.seal(err => {
    if (err) return callback(err);

    this.hooks.afterCompile.callAsync(compilation, err => {
        if (err) return callback(err);

        return callback(null, compilation);
    });
});

說明 compilation.seal主要是對chunk進行優(yōu)化,生成編譯后的源碼,比較重要,詳細展開如下所示

//代碼生成前面優(yōu)化
this.hooks.optimize.call();
this.hooks.optimizeTree.callAsync(this.chunks, this.modules, err => {
 
    this.hooks.beforeHash.call();
    this.createHash();
    this.hooks.afterHash.call();

    if (shouldRecord) this.hooks.recordHash.call(this.records);

    this.hooks.beforeModuleAssets.call();
    this.createModuleAssets();
    if (this.hooks.shouldGenerateChunkAssets.call() !== false) {
        this.hooks.beforeChunkAssets.call();
        //生成最終打包輸出的chunk資源,根據(jù)template文件,詳細步驟如下所示
        this.createChunkAssets();
    }
    
});
--------------------------------------
//取出最后文件需要的模板
const template = chunk.hasRuntime()
                    ? this.mainTemplate
                    : this.chunkTemplate;
//通過模板最終生成webpack_require格式的內(nèi)容,他這個是內(nèi)部封裝的拼接渲染邏輯,也沒用什么ejs,handlebar等這些模板工具
source = fileManifest.render();
//生成的資源保存在compilation.assets,方便下一步emitAssets步驟中,把文件輸出到硬盤
this.assets[file] = source;
    //把處理好的assets輸出到output的path中
    emitAssets(compilation, callback) {
        let outputPath;
    
        const emitFiles = err => {
            if (err) return callback(err);
    
            asyncLib.forEach(
                compilation.assets,
                (source, file, callback) => {
                    const writeOut = err => {
                        //輸出打包后的文件到配置中指定的目錄下
                        this.outputFileSystem.writeFile(targetPath, content, callback);
                    };
    
                    writeOut();
                }
            );
        };
    
        this.hooks.emit.callAsync(compilation, err => {
            if (err) return callback(err);
            outputPath = compilation.getPath(this.outputPath);
            this.outputFileSystem.mkdirp(outputPath, emitFiles);
        });
    }
總結(jié)

如果多帶帶看這篇文章的話,理解起來會比較困難,推薦一下與之相關(guān)的系列鋪墊文章,上面是我對webpack源碼運行流程的總結(jié), 整個流程已經(jīng)跑通了,不過還有蠻多點值得深入挖掘的。清明在家宅了3天,過得好快,明天公司組織去奧森公園尋寶行動,期待ing 。

推薦
webpack源碼之tapable
webpack源碼之plugin機制
webpack源碼之a(chǎn)st簡介
webpack源碼之loader機制

參考源碼
webpack: "4.4.1"
webpack-cli: "2.0.13"

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/94025.html

相關(guān)文章

  • webpack源碼分析四:plugin

    摘要:流程劃分縱觀整個打包過程,可以流程劃分為四塊。核心類關(guān)系圖功能實現(xiàn)模塊通過將源碼解析為樹并拆分,以及直至基礎(chǔ)模塊。通過的依賴和切割文件構(gòu)建出含有和包含關(guān)系的對象。通過模版完成主入口文件的寫入,模版完成切割文件的寫入。 前言 插件plugin,webpack重要的組成部分。它以事件流的方式讓用戶可以直接接觸到webpack的整個編譯過程。plugin在編譯的關(guān)鍵地方觸發(fā)對應(yīng)的事件,極大的...

    yhaolpz 評論0 收藏0
  • webpack原理

    摘要:原理查看所有文檔頁面前端開發(fā)文檔,獲取更多信息。初始化階段事件名解釋初始化參數(shù)從配置文件和語句中讀取與合并參數(shù),得出最終的參數(shù)。以上處理的相關(guān)配置如下編寫編寫的職責(zé)由上面的例子可以看出一個的職責(zé)是單一的,只需要完成一種轉(zhuǎn)換。 webpack原理 查看所有文檔頁面:前端開發(fā)文檔,獲取更多信息。原文鏈接:webpack原理,原文廣告模態(tài)框遮擋,閱讀體驗不好,所以整理成本文,方便查找。 工作...

    trigkit4 評論0 收藏0
  • Webpack源碼閱讀Tapable

    摘要:源碼分析安裝好包,根據(jù)上述方法,我們運行如下命令初始化在構(gòu)造函數(shù)處打上斷點,可以看到繼承自,上面定義了一個函數(shù)。因為函數(shù)定義在原型上,并通過在構(gòu)造函數(shù)中賦值。 Webpack源碼閱讀之Tapable webpack采用Tapable來進行流程控制,在這套體系上,內(nèi)部近百個插件有條不紊,還能支持外部開發(fā)自定義插件來擴展功能,所以在閱讀webpack源碼前先了解Tapable的機制是很有必...

    yanwei 評論0 收藏0
  • webpack源碼loader機制

    摘要:用于對模塊的源代碼進行轉(zhuǎn)換。甚至允許你直接在模塊中文件和區(qū)別之前一篇文章中介紹了機制和今天研究的對象他們兩者在一起極大的拓展了的功能。原理說明上面是源碼中執(zhí)行關(guān)鍵步驟遞歸的方式執(zhí)行執(zhí)行機流程似于中間件機制參考源碼參考文檔 loader概念 loader是用來加載處理各種形式的資源,本質(zhì)上是一個函數(shù), 接受文件作為參數(shù),返回轉(zhuǎn)化后的結(jié)構(gòu)。 loader 用于對模塊的源代碼進行轉(zhuǎn)換。loa...

    dmlllll 評論0 收藏0
  • ElementUI的構(gòu)建流程

    摘要:下面一步步拆解上述流程。切換至分支檢測本地和暫存區(qū)是否還有未提交的文件檢測本地分支是否有誤檢測本地分支是否落后遠程分支發(fā)布發(fā)布檢測到在分支上沒有沖突后,立即執(zhí)行。 背景 最近一直在著手做一個與業(yè)務(wù)強相關(guān)的組件庫,一直在思考要從哪里下手,怎么來設(shè)計這個組件庫,因為業(yè)務(wù)上一直在使用ElementUI(以下簡稱Element),于是想?yún)⒖剂艘幌翬lement組件庫的設(shè)計,看看Element構(gòu)...

    wudengzan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<