欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

webpack DefinePlugin源碼入口解析

 更新時(shí)間:2022年11月27日 10:39:23   作者:某時(shí)橙  
這篇文章主要為大家介紹了webpack DefinePlugin源碼入口解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

DefinePlugin是webpack的一個(gè)官方內(nèi)置插件,它允許在 編譯時(shí) 將你代碼中的變量替換為其他值或表達(dá)式。這在需要根據(jù)開發(fā)模式與生產(chǎn)模式進(jìn)行不同的操作時(shí),非常有用。例如,如果想在開發(fā)構(gòu)建中進(jìn)行日志記錄,而不在生產(chǎn)構(gòu)建中進(jìn)行,就可以定義一個(gè)全局常量去判斷是否記錄日志。這就是 DefinePlugin 的發(fā)光之處,設(shè)置好它,就可以忘掉開發(fā)環(huán)境和生產(chǎn)環(huán)境的構(gòu)建規(guī)則。

new webpack.DefinePlugin({
  PRODUCTION: JSON.stringify(true),
  VERSION: JSON.stringify('5fa3b9'),
  BROWSER_SUPPORTS_HTML5: true,
  TWO: '1+1',
  'typeof window': JSON.stringify('object'),
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
});

demo

console.log(PRODUCTION,VERSION,BROWSER_SUPPORTS_HTML5,TWO,typeof window,process.env.NODE_ENV);

源碼入口

parser是一個(gè)hookMap,它就相當(dāng)于一個(gè)管理hook的Map結(jié)構(gòu)。

    apply(compiler) {
        const definitions = this.definitions;
        compiler.hooks.compilation.tap(
            "DefinePlugin",
            (compilation, { normalModuleFactory }) => {
                //...
                normalModuleFactory.hooks.parser
                    .for("javascript/auto")
                    .tap("DefinePlugin", handler);
                normalModuleFactory.hooks.parser
                    .for("javascript/dynamic")
                    .tap("DefinePlugin", handler);
                normalModuleFactory.hooks.parser
                    .for("javascript/esm")
                    .tap("DefinePlugin", handler);                
                //...
            })
    }

parser的call時(shí)機(jī)在哪?完全就在于NormalModuleFactory.createParser時(shí)機(jī)

所以這個(gè)鉤子的語義就是parser創(chuàng)建時(shí)的初始化鉤子。

    createParser(type, parserOptions = {}) {
        parserOptions = mergeGlobalOptions(
            this._globalParserOptions,
            type,
            parserOptions
        );
        const parser = this.hooks.createParser.for(type).call(parserOptions);
        if (!parser) {
            throw new Error(`No parser registered for ${type}`);
        }
        this.hooks.parser.for(type).call(parser, parserOptions);
        return parser;
    }

好,現(xiàn)在讓我們看看具體初始化了什么邏輯。

首先現(xiàn)在program上定義一個(gè)鉤子,在遍歷JavaScript AST前(該時(shí)機(jī)由program定義位置所知),注冊(cè)buildInfo.valueDependencies=new Map();

并定義

buildInfo.valueDependencies.set(VALUE_DEP_MAIN, mainValue);

const handler = parser => {
                    const mainValue = compilation.valueCacheVersions.get(VALUE_DEP_MAIN);
    //mainValue是在DefinePlugin最初初始化時(shí)定義到compilation.valueCacheVersions上的
                    parser.hooks.program.tap("DefinePlugin", () => {
                        const { buildInfo } = parser.state.module;
                        if (!buildInfo.valueDependencies)
                            buildInfo.valueDependencies = new Map();
                        buildInfo.valueDependencies.set(VALUE_DEP_MAIN, mainValue);
                    });
//....
                    walkDefinitions(definitions, "");
}

然后開始遍歷Definitions(這是用戶提供的配置項(xiàng),比如 PRODUCTION: JSON.stringify(true),)

            const walkDefinitions = (definitions, prefix) => {
                        Object.keys(definitions).forEach(key => {
                            const code = definitions[key];
                            if (
                                code &&
                                typeof code === "object" &&
                                !(code instanceof RuntimeValue) &&
                                !(code instanceof RegExp)
                            ) {
                            //如果是對(duì)象就遞歸調(diào)用
                                walkDefinitions(code, prefix + key + ".");
                                applyObjectDefine(prefix + key, code);
                                return;
                            }
                            applyDefineKey(prefix, key);
                            applyDefine(prefix + key, code);
                        });
                    };

applyDefine

    const applyDefine = (key, code) => {
                        const originalKey = key;
                        const isTypeof = /^typeof\s+/.test(key);
                        if (isTypeof) key = key.replace(/^typeof\s+/, "");
                        let recurse = false;
                        let recurseTypeof = false;
                        if (!isTypeof) {
                            parser.hooks.canRename.for(key).tap("DefinePlugin", () => {
                                addValueDependency(originalKey);
                                return true;
                            });
                            parser.hooks.evaluateIdentifier
                                .for(key)
                                .tap("DefinePlugin", expr => {
                                    /**
                                     * this is needed in case there is a recursion in the DefinePlugin
                                     * to prevent an endless recursion
                                     * e.g.: new DefinePlugin({
                                     * "a": "b",
                                     * "b": "a"
                                     * });
                                     */
                                    if (recurse) return;
                                    addValueDependency(originalKey);
                                    recurse = true;
                                    const res = parser.evaluate(
                                        toCode(
                                            code,
                                            parser,
                                            compilation.valueCacheVersions,
                                            key,
                                            runtimeTemplate,
                                            null
                                        )
                                    );
                                    recurse = false;
                                    res.setRange(expr.range);
                                    return res;
                                });
                            parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
                                addValueDependency(originalKey);
                                const strCode = toCode(
                                    code,
                                    parser,
                                    compilation.valueCacheVersions,
                                    originalKey,
                                    runtimeTemplate,
                                    !parser.isAsiPosition(expr.range[0])
                                );
                                if (/__webpack_require__\s*(!?.)/.test(strCode)) {
                                    return toConstantDependency(parser, strCode, [
                                        RuntimeGlobals.require
                                    ])(expr);
                                } else if (/__webpack_require__/.test(strCode)) {
                                    return toConstantDependency(parser, strCode, [
                                        RuntimeGlobals.requireScope
                                    ])(expr);
                                } else {
                                    return toConstantDependency(parser, strCode)(expr);
                                }
                            });
                        }
                        parser.hooks.evaluateTypeof.for(key).tap("DefinePlugin", expr => {
                            /**
                             * this is needed in case there is a recursion in the DefinePlugin
                             * to prevent an endless recursion
                             * e.g.: new DefinePlugin({
                             * "typeof a": "typeof b",
                             * "typeof b": "typeof a"
                             * });
                             */
                            if (recurseTypeof) return;
                            recurseTypeof = true;
                            addValueDependency(originalKey);
                            const codeCode = toCode(
                                code,
                                parser,
                                compilation.valueCacheVersions,
                                originalKey,
                                runtimeTemplate,
                                null
                            );
                            const typeofCode = isTypeof
                                ? codeCode
                                : "typeof (" + codeCode + ")";
                            const res = parser.evaluate(typeofCode);
                            recurseTypeof = false;
                            res.setRange(expr.range);
                            return res;
                        });
                        parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
                            addValueDependency(originalKey);
                            const codeCode = toCode(
                                code,
                                parser,
                                compilation.valueCacheVersions,
                                originalKey,
                                runtimeTemplate,
                                null
                            );
                            const typeofCode = isTypeof
                                ? codeCode
                                : "typeof (" + codeCode + ")";
                            const res = parser.evaluate(typeofCode);
                            if (!res.isString()) return;
                            return toConstantDependency(
                                parser,
                                JSON.stringify(res.string)
                            ).bind(parser)(expr);
                        });
                    };

hooks.expression

在applyDefine中定義的hooks.expression定義了對(duì)表達(dá)式的替換處理。

當(dāng)代碼解析到語句【key】時(shí),便會(huì)觸發(fā)如下鉤子邏輯,不過先別急,我們先搞清楚expression鉤子在何處會(huì)被觸發(fā)。

parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
//...
                        }

觸發(fā)時(shí)機(jī)

單單指demo中的情況

比如PRODUCTION會(huì)被acron解析為Identifier

而在parse階段中,會(huì)有這么一句

        if (this.hooks.program.call(ast, comments) === undefined) {
            //...其他解析語句
            this.walkStatements(ast.body);
        }
//然后會(huì)走到這
     walkIdentifier(expression) {
        this.callHooksForName(this.hooks.expression, expression.name, expression);
    }
//最后
        const hook = hookMap.get(name);//獲取hook
        if (hook !== undefined) {
            const result = hook.call(...args); //執(zhí)行hook
            if (result !== undefined) return result;
        }

具體邏輯

    parser.hooks.expression.for(key).tap("DefinePlugin", expr =>{
        addValueDependency(originalKey);
        const strCode = toCode(
            code,
            parser,
            compilation.valueCacheVersions,
            originalKey,
            runtimeTemplate,
            !parser.isAsiPosition(expr.range[0])
        );
        if (/__webpack_require__\s*(!?.)/.test(strCode)) {
            return toConstantDependency(parser, strCode, [
                RuntimeGlobals.require
            ])(expr);
        } else if (/__webpack_require__/.test(strCode)) {
            return toConstantDependency(parser, strCode, [
                RuntimeGlobals.requireScope
            ])(expr);
        } else {
            return toConstantDependency(parser, strCode)(expr);
        }
    });
}

addValueDependency

//這里會(huì)影響needBuild的邏輯,是控制是否構(gòu)建模塊的      
const addValueDependency = key => {
                        const { buildInfo } = parser.state.module;
                        //這里就可以理解為設(shè)置key,value
                        buildInfo.valueDependencies.set(
                            VALUE_DEP_PREFIX + key,
                            compilation.valueCacheVersions.get(VALUE_DEP_PREFIX + key)
                        );
                    };

要搞懂a(chǎn)ddValueDependency到底做了什么,首先得理解

  • compilation.valueCacheVersions這個(gè)map結(jié)構(gòu)做了什么?
  • buildInfo.valueDependencies這里的依賴收集起來有什么用?

toCode獲取strCode

toConstantDependency

設(shè)置ConstDependency靜態(tài)轉(zhuǎn)換依賴。

exports.toConstantDependency = (parser, value, runtimeRequirements) => {
    return function constDependency(expr) {
        const dep = new ConstDependency(value, expr.range, runtimeRequirements);
        dep.loc = expr.loc;
        parser.state.module.addPresentationalDependency(dep);
        return true;
    };
};

ConstDependency是如何替換源碼的?

出處在seal階段

        if (module.presentationalDependencies !== undefined) {
            for (const dependency of module.presentationalDependencies) {
                this.sourceDependency(
                    module,
                    dependency,
                    initFragments,
                    source,
                    generateContext
                );
            }
        }

sourceDenpendency,會(huì)獲取依賴上的執(zhí)行模板

        const constructor = /** @type {new (...args: any[]) => Dependency} */ (
            dependency.constructor
        );
        //template可以理解為代碼生成模板
        const template = generateContext.dependencyTemplates.get(constructor);
///....
template.apply(dependency, source, templateContext);//然后執(zhí)行

而ConstPendency的執(zhí)行模板直接替換了源碼中的某個(gè)片段內(nèi)容

const dep = /** @type {ConstDependency} */ (dependency);
        if (dep.runtimeRequirements) {
            for (const req of dep.runtimeRequirements) {
                templateContext.runtimeRequirements.add(req);
            }
        }
        if (typeof dep.range === "number") {
            source.insert(dep.range, dep.expression);
            return;
        }
        source.replace(dep.range[0], dep.range[1] - 1, dep.expression);

hooks.canRename

在applyDefine中,也有hooks.canRename的調(diào)用:

    parser.hooks.canRename.for(key).tap("DefinePlugin", () => {
                            addValueDependency(key);
                            return true;
   });

在這里其實(shí)就是允許key可以被重命名,并借機(jī)收集key作為依賴,但這個(gè)重命名的工作不是替換,并不在definePlugin內(nèi)做,有點(diǎn)點(diǎn)奇怪。

詳細(xì):

該hook會(huì)在js ast遍歷時(shí)多處被call

  • walkAssignmentExpression 賦值表達(dá)式
  • _walkIIFE CallExpression 函數(shù)調(diào)用表達(dá)式發(fā)現(xiàn)是IIFE時(shí)
  • walkVariableDeclaration 聲明語句

canRename有什么用?其實(shí)是配合rename使用的鉤子

當(dāng)其返回true,rename鉤子才能起作用。如下:

    walkVariableDeclaration(statement) {
        for (const declarator of statement.declarations) {
            switch (declarator.type) {
                case "VariableDeclarator": {
                    const renameIdentifier =
                        declarator.init && this.getRenameIdentifier(declarator.init);
                    if (renameIdentifier && declarator.id.type === "Identifier") {
                        const hook = this.hooks.canRename.get(renameIdentifier);
                        if (hook !== undefined && hook.call(declarator.init)) {
                            // renaming with "var a = b;"
                            const hook = this.hooks.rename.get(renameIdentifier);
                            if (hook === undefined || !hook.call(declarator.init)) {
                                this.setVariable(declarator.id.name, renameIdentifier);
                            }
                            break;
                        }
                    }
                   //...
                }
            }
        }
    }

官方也有所介紹這個(gè)鉤子

hooks.typeof

                        parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
                            addValueDependency(originalKey);
                            const codeCode = toCode(
                                code,
                                parser,
                                compilation.valueCacheVersions,
                                originalKey,
                                runtimeTemplate,
                                null
                            );
                            const typeofCode = isTypeof
                                ? codeCode
                                : "typeof (" + codeCode + ")";
                            const res = parser.evaluate(typeofCode);
                            if (!res.isString()) return;
                            return toConstantDependency(
                                parser,
                                JSON.stringify(res.string)
                            ).bind(parser)(expr);
                        });

先執(zhí)行typeof再用toConstantDependency替換。。

以上就是webpack DefinePlugin源碼入口解析的詳細(xì)內(nèi)容,更多關(guān)于webpack DefinePlugin入口的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript檢測(cè)用戶是否在線的6種方法總結(jié)

    JavaScript檢測(cè)用戶是否在線的6種方法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了JavaScript中實(shí)現(xiàn)檢測(cè)用戶是否在線的6種常用方法,文中的示例代碼講解詳細(xì),感興趣的可以跟隨小編一起學(xué)習(xí)一下
    2023-08-08
  • js漢字轉(zhuǎn)拼音實(shí)現(xiàn)代碼

    js漢字轉(zhuǎn)拼音實(shí)現(xiàn)代碼

    漢字轉(zhuǎn)拼音,比較娛樂的一款應(yīng)用,感興趣的朋友可以了解下,或許對(duì)你學(xué)習(xí)js有所幫助
    2013-02-02
  • 使用Echarts繪制動(dòng)態(tài)排序折線圖

    使用Echarts繪制動(dòng)態(tài)排序折線圖

    Echarts,作為一個(gè)使用廣泛的JavaScript圖表庫,提供了創(chuàng)建動(dòng)態(tài)排序折線圖的強(qiáng)大功能,下面就跟隨小編一起學(xué)習(xí)一下如何使用Echarts繪制動(dòng)態(tài)排序折線圖吧
    2023-12-12
  • 總結(jié)在前端排序中遇到的問題

    總結(jié)在前端排序中遇到的問題

    這篇文章給大家羅列了在前段排序中會(huì)遇到的問題并寫了解決方案,非常詳細(xì),有需要的朋友可以參考。
    2016-07-07
  • Node調(diào)試工具JSHint的安裝及配置教程

    Node調(diào)試工具JSHint的安裝及配置教程

    Node的優(yōu)勢(shì)我就不再亂吹捧了,它讓javascript統(tǒng)一web的前后臺(tái)成為了可能。但是對(duì)于新手來說,server端的JS代碼可能不像client端的代碼那么好調(diào)試,直觀。client端JS代碼的調(diào)試基本上經(jīng)歷了一個(gè)從“肉眼--alert()--firebug(或者其它的developer tools)”的一個(gè)過程。而對(duì)于server端的調(diào)試,可能新手仍然停留在使用“肉眼--console()”的階段。其實(shí),Node經(jīng)過了這么多年(雖然才短短幾年)的發(fā)展,也有了很多不錯(cuò)的第三方的調(diào)試工具。包括Node內(nèi)建的調(diào)試工具debugger、node-inspector等。
    2014-05-05
  • javascript開發(fā)中因空格引發(fā)的錯(cuò)誤

    javascript開發(fā)中因空格引發(fā)的錯(cuò)誤

    最近寫一個(gè)關(guān)于用JavaScript做圖片自動(dòng)切換問題發(fā)現(xiàn)一個(gè)非常奇特的問題,除了空格和換行外完全相同的代碼,在Firefox下卻有截然不同的運(yùn)行結(jié)果,今天記錄以提供他人留意及自我備查。
    2010-11-11
  • javascript實(shí)現(xiàn)信息增刪改查的方法

    javascript實(shí)現(xiàn)信息增刪改查的方法

    這篇文章主要介紹了javascript實(shí)現(xiàn)信息增刪改查的方法,實(shí)例分析了javascript操作頁面元素實(shí)現(xiàn)針對(duì)頁面信息的增刪改查功能,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • js wmp操作代碼小結(jié)(音樂連播功能)

    js wmp操作代碼小結(jié)(音樂連播功能)

    js對(duì)于wmp的一些屬性與方法,方便大家控制音樂的播放的一些細(xì)節(jié)
    2008-11-11
  • 小程序?qū)崿F(xiàn)多列選擇器

    小程序?qū)崿F(xiàn)多列選擇器

    這篇文章主要為大家詳細(xì)介紹了小程序?qū)崿F(xiàn)多列選擇器的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-02-02
  • IE,firefox(火狐)瀏覽器無提示關(guān)閉窗口js實(shí)現(xiàn)代碼小結(jié)

    IE,firefox(火狐)瀏覽器無提示關(guān)閉窗口js實(shí)現(xiàn)代碼小結(jié)

    在不是js打開的頁面上按window.close(),會(huì)有提示框,很煩,現(xiàn)在可以不用了,沒有提示框直接關(guān)閉窗口。下面腳本之家編輯特為大家整理了一些。
    2009-09-09

最新評(píng)論