webpack 运行时模版 第 二 节 /lib/RuntimeTemplate.js

  • 构造函数 constructor

    • 初始化 compilationoutputOptionsrequestShortener
    • 设置 globalObject(全局对象名,如 windowglobalThis
    • 构造 content hash 的占位符字符串(如 XXXXXXXX

  • 环境能力判断方法

    • isIIFE():是否使用 IIFE 包裹输出代码
    • isModule():是否输出为 ESM 模块格式
    • isNeutralPlatform():是否为非浏览器、非 Node 的中立平台
    • supportsConst():是否支持 const
    • supportsArrowFunction():是否支持箭头函数
    • supportsAsyncFunction():是否支持 async 函数
    • supportsOptionalChaining():是否支持可选链 ?.
    • supportsForOf():是否支持 for...of
    • supportsDestructuring():是否支持解构赋值
    • supportsBigIntLiteral():是否支持 BigInt
    • supportsDynamicImport():是否支持 import()
    • supportsEcmaScriptModuleSyntax():是否支持原生模块语法
    • supportTemplateLiteral():是否支持模板字符串
    • supportNodePrefixForCoreModules():是否支持 node: 前缀

  • 函数生成工具方法

    • returningFunction(returnValue, args):生成一个返回指定值的函数
    • basicFunction(args, body):生成一个普通函数或箭头函数
    • expressionFunction(expression, args):生成包含表达式的函数体代码
    • emptyFunction():生成空函数代码
    • iife(args, body):生成 IIFE(立即执行函数表达式)

  • 代码片段生成方法

    • concatenation(...args):根据目标环境选择模板字符串或 ES5 拼接
    • _es5Concatenation(args):使用 + 拼接字符串/表达式(兼容旧环境)
    • destructureArray(items, value):生成数组解构赋值或兼容代码
    • destructureObject(items, value):生成对象解构赋值或兼容代码
    • forEach(variable, array, body):生成 for...ofArray.prototype.forEach 循环
js 复制代码
/**
 * 构造函数
 * @param {Compilation} compilation 当前的编译对象
 * @param {OutputOptions} outputOptions 输出配置项
 * @param {RequestShortener} requestShortener 请求路径简化工具
 */
constructor(compilation, outputOptions, requestShortener) {
    this.compilation = compilation;
    this.outputOptions = /** @type {OutputOptions} */ (outputOptions || {});
    this.requestShortener = requestShortener;

    // 获取运行时的全局对象(如 window、globalThis 等)
    this.globalObject = /** @type {string} */ (getGlobalObject(outputOptions.globalObject));

    // 构建 contentHash 占位符,例如 "XXXXXXXX"
    this.contentHashReplacement = "X".repeat(
        /** @type {NonNullable<OutputOptions["hashDigestLength"]>} */
        (outputOptions.hashDigestLength)
    );
}

// 判断是否使用 IIFE(立即执行函数表达式)包裹输出代码
isIIFE() {
    return this.outputOptions.iife;
}

// 判断是否生成 ESM(原生模块)语法
isModule() {
    return this.outputOptions.module;
}

// 判断是否是中立平台(如无 document 对象,非 node 环境)
isNeutralPlatform() {
    return (
        !this.outputOptions.environment.document &&
        !this.compilation.compiler.platform.node
    );
}

// 以下为环境能力判断函数,根据配置判断是否支持某些 JS 特性
supportsConst() {
    return this.outputOptions.environment.const;
}

supportsArrowFunction() {
    return this.outputOptions.environment.arrowFunction;
}

supportsAsyncFunction() {
    return this.outputOptions.environment.asyncFunction;
}

supportsOptionalChaining() {
    return this.outputOptions.environment.optionalChaining;
}

supportsForOf() {
    return this.outputOptions.environment.forOf;
}

supportsDestructuring() {
    return this.outputOptions.environment.destructuring;
}

supportsBigIntLiteral() {
    return this.outputOptions.environment.bigIntLiteral;
}

supportsDynamicImport() {
    return this.outputOptions.environment.dynamicImport;
}

supportsEcmaScriptModuleSyntax() {
    return this.outputOptions.environment.module;
}

supportTemplateLiteral() {
    return this.outputOptions.environment.templateLiteral;
}

supportNodePrefixForCoreModules() {
    return this.outputOptions.environment.nodePrefixForCoreModules;
}

/**
 * 返回一个返回指定值的函数字符串表示
 * @param {string} returnValue 返回值
 * @param {string} args 参数名
 * @returns {string} 函数字符串
 */
returningFunction(returnValue, args = "") {
    return this.supportsArrowFunction()
        ? `(${args}) => (${returnValue})`
        : `function(${args}) { return ${returnValue}; }`;
}

/**
 * 返回一个基本函数代码字符串
 * @param {string} args 参数名
 * @param {string|string[]} body 函数体内容
 * @returns {string} 函数字符串
 */
basicFunction(args, body) {
    return this.supportsArrowFunction()
        ? `(${args}) => {\n${Template.indent(body)}\n}`
        : `function(${args}) {\n${Template.indent(body)}\n}`;
}

/**
 * 拼接表达式或字符串
 * @param {Array<string|{expr: string}>} args 拼接的内容
 * @returns {string} 拼接后的表达式字符串
 */
concatenation(...args) {
    const len = args.length;

    if (len === 2) return this._es5Concatenation(args);
    if (len === 0) return '""';
    if (len === 1) {
        return typeof args[0] === "string"
            ? JSON.stringify(args[0])
            : `"" + ${args[0].expr}`;
    }
    if (!this.supportTemplateLiteral()) return this._es5Concatenation(args);

    // 模板字符串与字符串拼接的开销分析(用于做生成方式的选择)
    let templateCost = 0;
    let concatenationCost = 0;
    let lastWasExpr = false;

    for (const arg of args) {
        const isExpr = typeof arg !== "string";
        if (isExpr) {
            templateCost += 3;
            concatenationCost += lastWasExpr ? 1 : 4;
        }
        lastWasExpr = isExpr;
    }

    if (lastWasExpr) concatenationCost -= 3;
    if (typeof args[0] !== "string" && typeof args[1] === "string")
        concatenationCost -= 3;

    if (concatenationCost <= templateCost) return this._es5Concatenation(args);

    return `\`${args.map(arg => (typeof arg === "string" ? arg : `\${${arg.expr}}`)).join("")}\``;
}

/**
 * 使用旧的字符串拼接方式
 * @param {Array<string|{expr: string}>} args 拼接项(长度 >= 2)
 * @returns {string} 拼接表达式
 * @private
 */
_es5Concatenation(args) {
    const str = args.map(arg => (typeof arg === "string" ? JSON.stringify(arg) : arg.expr)).join(" + ");
    // 若前两个都是表达式,则需强制转字符串防止数值相加
    return typeof args[0] !== "string" && typeof args[1] !== "string"
        ? `"" + ${str}`
        : str;
}

/**
 * 返回一个包含表达式的函数
 * @param {string} expression 表达式
 * @param {string} args 参数
 * @returns {string} 函数字符串
 */
expressionFunction(expression, args = "") {
    return this.supportsArrowFunction()
        ? `(${args}) => (${expression})`
        : `function(${args}) { ${expression}; }`;
}

/**
 * 返回一个空函数
 * @returns {string} 空函数代码
 */
emptyFunction() {
    return this.supportsArrowFunction() ? "x => {}" : "function() {}";
}

/**
 * 解构数组形式赋值
 * @param {string[]} items 变量名数组
 * @param {string} value 被解构的数组表达式
 * @returns {string} 解构代码
 */
destructureArray(items, value) {
    return this.supportsDestructuring()
        ? `var [${items.join(", ")}] = ${value};`
        : Template.asString(items.map((item, i) => `var ${item} = ${value}[${i}];`));
}

/**
 * 解构对象形式赋值
 * @param {string[]} items 变量名数组
 * @param {string} value 被解构的对象表达式
 * @returns {string} 解构代码
 */
destructureObject(items, value) {
    return this.supportsDestructuring()
        ? `var {${items.join(", ")}} = ${value};`
        : Template.asString(items.map(item => `var ${item} = ${value}${propertyAccess([item])};`));
}

/**
 * 创建 IIFE(立即执行函数表达式)代码
 * @param {string} args 参数
 * @param {string} body 函数体
 * @returns {string} IIFE 代码
 */
iife(args, body) {
    return `(${this.basicFunction(args, body)})()`;
}

/**
 * 构建 forEach 代码,支持 for-of 或 Array.prototype.forEach
 * @param {string} variable 遍历的变量名
 * @param {string} array 被遍历的数组
 * @param {string|string[]} body 循环体代码
 * @returns {string} 生成的 forEach 代码
 */
forEach(variable, array, body) {
    return this.supportsForOf()
        ? `for(const ${variable} of ${array}) {\n${Template.indent(body)}\n}`
        : `${array}.forEach(function(${variable}) {\n${Template.indent(body)}\n});`;
}
相关推荐
Larcher28 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐41 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭1 小时前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu2 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花2 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程