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

資訊專欄INFORMATION COLUMN

webpack源碼分析(一)-流程分析

codecraft / 539人閱讀

摘要:先上一張流程圖一般打包文件是通過(guò)調(diào)用這實(shí)際上等同于通過(guò)調(diào)用源碼如下將用戶本地的配置文件拼接上內(nèi)置的參數(shù)初始化對(duì)象編輯器對(duì)象,包含所有主環(huán)境相關(guān)內(nèi)容注冊(cè)插件和用戶配置的插件觸發(fā)和上注冊(cè)的事件注冊(cè)內(nèi)置插件源碼如下注冊(cè)觸發(fā)鉤子觸發(fā)鉤子觸發(fā)鉤子

先上一張流程圖

一般webpack打包文件是通過(guò)cli調(diào)用

 webpack.js --config=webpack.build.js

這實(shí)際上等同于通過(guò)node調(diào)用

const Webpack = require("./node_modules/webpack");
const config = require("./config1.js");
const compiler = Webpack(config);
compiler.run();

Webpack(config)源碼如下:

const webpack = (options, callback) => {
    //將用戶本地的配置文件拼接上webpack內(nèi)置的參數(shù)
    options = new WebpackOptionsDefaulter().process(options);
    //初始化compiler對(duì)象(webpack編輯器對(duì)象,包含所有webpack主環(huán)境相關(guān)內(nèi)容)
    compiler = new Compiler(options.context);
    compiler.options = options;
    //注冊(cè)NodeEnvironmentPlugin插件和用戶配置的插件
    new NodeEnvironmentPlugin().apply(compiler);
    if (options.plugins && Array.isArray(options.plugins)) {
        for (const plugin of options.plugins) {
            plugin.apply(compiler);
        }
    }
    //觸發(fā)environment和afterEnvironment上注冊(cè)的事件
    compiler.hooks.environment.call();
    compiler.hooks.afterEnvironment.call();
    //注冊(cè)webpack內(nèi)置插件,源碼如下
    compiler.options = new WebpackOptionsApply().process(options, compiler);
    return compiler;
})

class WebpackOptionsApply extends OptionsApply {
    process(options, compiler) {
        //注冊(cè)EntryOptionPlugin
        new EntryOptionPlugin().apply(compiler);
        //觸發(fā)entryOption鉤子
        var a = compiler.hooks.entryOption.call(options.context, options.entry);
        //觸發(fā)afterPlugins鉤子
        compiler.hooks.afterPlugins.call(compiler);
        //觸發(fā)afterResolvers鉤子
        compiler.hooks.afterResolvers.call(compiler);
    }
}

主要是初始化compiler對(duì)象和注冊(cè)插件,下面介紹下EntryOptionPlugin插件

EntryOptionPlugin.apply方法
apply(compiler) {
    //將回調(diào)函數(shù)注冊(cè)到hooks.entryOption上
    //上文調(diào)用compiler.hooks.entryOption.call(options.context, options.entry)時(shí)觸發(fā)
    compiler.hooks.entryOption.tap("EntryOptionPlugin", (context, entry) => {
        //取出entry文件入口配置,判斷是否數(shù)組,調(diào)用對(duì)應(yīng)的插件
        for (const name of Object.keys(entry)) {
            itemToPlugin(context, entry[name], name).apply(compiler);
        }
    }
}
const itemToPlugin = (context, item, name) => {
    if (Array.isArray(item)) {
        return new MultiEntryPlugin(context, item, name);
    }
    return new SingleEntryPlugin(context, item, name);
}
//本文介紹entry[name]為字符串的情況,調(diào)用new SingleEntryPlugin().apply方法,源碼如下
apply(compiler) {
    //在compilation鉤子上注冊(cè)回調(diào),compilation.call時(shí)觸發(fā)
    compiler.hooks.compilation.tap(
        "SingleEntryPlugin",
        (compilation, { normalModuleFactory }) => {
            //設(shè)置SingleEntryDependency使用normalModuleFactory創(chuàng)建Module
            compilation.dependencyFactories.set(
                SingleEntryDependency,
                normalModuleFactory
            );
        }
    );
    compiler.hooks.make.tapAsync(
        "SingleEntryPlugin",
        (compilation, callback) => {
            const { entry, name, context } = this;

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

經(jīng)過(guò)上一步的分析可以對(duì)webpack的插件機(jī)制有一定的了解,插件主要是掛載一些回調(diào)函數(shù)在compiler的生命周期上,當(dāng)執(zhí)行到該階段時(shí)觸發(fā)(事件的發(fā)布訂閱,繼承自tapable)。
compiler的生命周期可參考:webpack hooks,下面再看下compiler.run()方法

run(callback) {
    this.compile(onCompiled);
}

compile(callback) {
    //初始化compilation,compilation對(duì)象代表了一次單一的版本構(gòu)建和生成資源過(guò)程
    const compilation = this.newCompilation(params);
    // 觸發(fā)注冊(cè)在make上的事件函數(shù),
    this.hooks.make.callAsync(compilation, err => {
        //make上注冊(cè)的事件執(zhí)行完畢后觸發(fā)回調(diào),源碼后面給出
    }
}
//觸發(fā)上文提到的SingleEntryPlugin注冊(cè)事件
compiler.hooks.make.tapAsync(
    "SingleEntryPlugin",
    (compilation, callback) => {
        const { entry, name, context } = this;
        // 入口文件的依賴對(duì)象,
        const dep = SingleEntryPlugin.createDependency(entry, name);
        compilation.addEntry(context, dep, name, callback);
    }
);

addEntry(context, entry, name, callback) {
        this._addModuleChain(context, dep, ...)
}

_addModuleChain(context, dependency, onModule, callback) {
    //獲取dependency
    const Dep = /** @type {DepConstructor} */ (dependency.constructor);
    //獲取moduleFactory,根據(jù)上文的介紹此處是normalModuleFactory
    const moduleFactory = this.dependencyFactories.get(Dep);
    //獲取module
    moduleFactory.create((err, module) => {
        dependency.module = module;
        this.buildModule(module, false, null, null, err => {
            //初始化moudle后生成ast對(duì)象,計(jì)算依賴,后面介紹
        })
    )
}
//獲取module的實(shí)現(xiàn)
//normalModuleFactory.create
create(data, callback) {
    // 獲取在constructor中注冊(cè)的factory方法
    const factory = this.hooks.factory.call(null);
    factory(result, (err, module) => {})
}

class NormalModuleFactory extends Tapable {
    constructor(context, resolverFactory, options) {
        this.hooks.factory.tap("NormalModuleFactory", () => (result, callback) => {
            //返回初始的module對(duì)象
            callback(null, {
                context: context,
                request: loaders
                    .map(loaderToIdent)
                    .concat([resource])
                    .join("!"),
                dependencies: data.dependencies,
                ...
            });
        }
    }
}

buildModule回調(diào)

this.buildModule(module, false, null, null, err => {
    // 根據(jù)js代碼獲取ast語(yǔ)法樹(shù)對(duì)象
    ast = acorn.parse(code, parserOptions);
    // 根據(jù)ast加載模塊的依賴
    this.prewalkStatements(ast.body);
    this.walkStatements(ast.body);

make主要是以entry為入口,生成一個(gè)modoule對(duì)象,其中的關(guān)鍵是根據(jù)js代碼生成ast語(yǔ)法樹(shù)對(duì)象,同時(shí)分析語(yǔ)法樹(shù)加載需要使用到的依賴(dependency),如果存在import依賴,就會(huì)生成新的modoule,知道所有依賴加在完畢,下圖是部分dependency示例

make階段完成之后會(huì)進(jìn)入seal階段

this.hooks.make.callAsync(compilation, err => {
    compilation.seal(err => {})
})
seal() {
    for (const preparedEntrypoint of this._preparedEntrypoints) {
        const module = preparedEntrypoint.module;
        const name = preparedEntrypoint.name;
        const chunk = this.addChunk(name);
        chunk.entryModule = module;
    }
    this.createChunkAssets();
}
createChunkAssets(){
   const manifest = template.getRenderManifest({
        chunk,
        hash: this.hash,
        fullHash: this.fullHash,
        outputOptions,
        moduleTemplates: this.moduleTemplates,
        dependencyTemplates: this.dependencyTemplates
    }); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
    for (const fileManifest of manifest) {
        source = fileManifest.render();
    }
}

compile結(jié)束后調(diào)用compiler.emitAssets

emitAssets() {
    const targetPath = this.outputFileSystem.join(
        outputPath,
        targetFile
    );
    let content = source.source();
    //this.writeFile = fs.writeFile.bind(fs);
    this.outputFileSystem.writeFile(targetPath, content, callback);
}

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

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

相關(guān)文章

  • webpack源碼之運(yùn)行流程

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

    kviccn 評(píng)論0 收藏0
  • Webpack 源碼(二)—— 如何閱讀源碼

    摘要:正所謂四兩撥千斤,找對(duì)要分析的對(duì)象以及它的關(guān)系網(wǎng),就找到了正確的分析源碼的方法下面的是我的公眾號(hào)二維碼圖片,歡迎關(guān)注。 1、如何調(diào)試閱讀源碼 如果想要了解 Webpack 的流程,只要閱讀 @七玨 細(xì)說(shuō) webpack 之流程篇 所述的內(nèi)容就夠了,講解地比較全面了;本文就不對(duì) Webpack 流程再做重復(fù)的描述,而是從另外一個(gè)角度補(bǔ)充分析 Webpack 源碼; Webpack 中最為...

    wing324 評(píng)論0 收藏0
  • 用十分之的構(gòu)建時(shí)間做場(chǎng)頁(yè)面靜態(tài)資源依賴分析

    摘要:不直接使用的原因很簡(jiǎn)單首先構(gòu)建一次實(shí)在太慢了,特別是有幾十個(gè)頁(yè)面存在的情況下,另一個(gè)原因是我只是想拿到資源依賴,我根本不想對(duì)整個(gè)前端進(jìn)行一次構(gòu)建,也不想生成任何。這就達(dá)到了本文題目中目的,用十分之一的構(gòu)建時(shí)間做一場(chǎng)頁(yè)面靜態(tài)資源依賴分析。原文鏈接 作者:梯田 前言: 所謂【靜態(tài)資源依賴分析】,指的是可以通過(guò)分析頁(yè)面資源后,可以以 json 數(shù)據(jù)或者圖表的方式拿到頁(yè)面資源間的依賴關(guān)系。 比如 c...

    B0B0 評(píng)論0 收藏0
  • webpack源碼分析之四:plugin

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

    yhaolpz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<