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});`;
}
相关推荐
昔冰_G5 分钟前
解锁webpack:对html、css、js及图片资源的抽离打包处理
前端·javascript·css·webpack·npm·html·打包
萌萌哒草头将军12 分钟前
🚀 REST API 还是 ✈️ GraphQL ❓
前端·vue.js·react.js
just小千25 分钟前
重学React(一):描述UI
前端·react.js·ui
fakaifa43 分钟前
【最新版】沃德代驾源码全开源+前端uniapp
前端·小程序·uni-app·开源·php·沃德代驾·代驾小程序
清羽_ls1 小时前
leetcode-位运算
前端·算法·leetcode·位运算
李菠菜1 小时前
利用Nginx实现高性能的前端打点采集服务(支持GET和POST)
linux·前端·nginx
lilye661 小时前
精益数据分析(6/126):深入理解精益分析的核心要点
前端·人工智能·数据分析
Apifox1 小时前
Apifox 4月更新|Apifox在线文档支持LLMs.txt、评论支持使用@提及成员、支持为团队配置「IP 允许访问名单」
前端·后端·ai编程
Jolyne_1 小时前
搭建公司前端脚手架
前端·架构·前端框架
hang_bro1 小时前
el-tree的动态加载问题与解决
前端·javascript·vue.js