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

blockPromise(options)

  • 功能:生成用于加载 chunk 的 Promise 表达式。

  • 用途 :支持异步模块(如 import())在 runtime 中正确加载 chunk。

  • 逻辑

    • 如果没有 block 或 block 对应的 chunkGroup 为空,返回 Promise.resolve(...)
    • 获取 block 所属的 chunkGroup,并筛选出没有 runtime 且有 ID 的 chunk。
    • 若只需加载一个 chunk,生成 __webpack_require__.e(chunkId) 表达式。
    • 若有多个 chunk,生成 Promise.all([...]) 包含多个 ensureChunk 调用。
    • 自动收集 RuntimeGlobals.ensureChunk 和可能的 RuntimeGlobals.hasFetchPriority 到 runtimeRequirements 中。

asyncModuleFactory(options)

  • 功能:生成异步模块的工厂函数代码。

  • 用途:用于代码分割中异步模块的运行时代码生成。

  • 逻辑

    • 获取异步 block 的依赖对应模块。
    • 调用 blockPromise 获取 chunk 加载表达式。
    • 创建工厂函数(实际调用 __webpack_require__ 载入模块)。
    • 若 chunk 加载是异步的,使用 .then(factory) 包裹;否则直接返回工厂函数。

syncModuleFactory(options)

  • 功能:生成同步模块的工厂函数代码。

  • 用途:用于普通(非异步)模块的运行时加载工厂。

  • 逻辑

    • 获取依赖模块。
    • 构建 (() => __webpack_require__(moduleId)) 的函数形式。

defineEsModuleFlagStatement(options)

  • 功能 :生成标记 exports.__esModule = true 的语句。

  • 用途 :兼容 ESModule 模式,确保 importrequire 之间正确识别模块类型。

  • 逻辑

    • 添加对 RuntimeGlobals.makeNamespaceObjectRuntimeGlobals.exports 的依赖。
    • 返回调用 __webpack_require__.r(exports) 的代码。
js 复制代码
class RuntimeTemplate {
	/**
	 * @param {object} options options 配置项
	 * @param {AsyncDependenciesBlock | undefined} options.block 异步模块块(如 import())
	 * @param {string} options.message 注释信息
	 * @param {ChunkGraph} options.chunkGraph Chunk 图结构(模块和 chunk 的依赖关系)
	 * @param {RuntimeRequirements} options.runtimeRequirements 存储运行时代码所需依赖
	 * @returns {string} 返回用于异步模块加载的代码字符串
	 */
	blockPromise({ block, message, chunkGraph, runtimeRequirements }) {
		// 如果 block 不存在,直接返回一个已完成的 Promise
		if (!block) {
			const comment = this.comment({ message });
			return `Promise.resolve(${comment.trim()})`;
		}

		// 获取该 block 所对应的 chunk group(一个或多个 chunk 的集合)
		const chunkGroup = chunkGraph.getBlockChunkGroup(block);
		// 若 chunkGroup 不存在或为空,也返回已完成 Promise
		if (!chunkGroup || chunkGroup.chunks.length === 0) {
			const comment = this.comment({ message });
			return `Promise.resolve(${comment.trim()})`;
		}

		// 过滤掉包含 runtime 的 chunk,并只保留有 id 的 chunk
		const chunks = chunkGroup.chunks.filter(
			chunk => !chunk.hasRuntime() && chunk.id !== null
		);

		// 添加注释信息,包括 chunkName
		const comment = this.comment({
			message,
			chunkName: block.chunkName
		});

		// 如果只需加载一个 chunk
		if (chunks.length === 1) {
			const chunkId = JSON.stringify(chunks[0].id);
			// 添加 ensureChunk 到运行时依赖(__webpack_require__.e)
			runtimeRequirements.add(RuntimeGlobals.ensureChunk);

			// 获取 fetch 优先级(用于 HTTP/2 优化)
			const fetchPriority = chunkGroup.options.fetchPriority;
			if (fetchPriority) {
				runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
			}

			// 返回加载单个 chunk 的表达式
			return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${
				fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
			})`;
		}
		// 如果有多个 chunk 需要加载
		else if (chunks.length > 0) {
			runtimeRequirements.add(RuntimeGlobals.ensureChunk);
			const fetchPriority = chunkGroup.options.fetchPriority;
			if (fetchPriority) {
				runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
			}

			/**
			 * @param {Chunk} chunk 单个 chunk
			 * @returns {string} 返回 ensureChunk 加载语句
			 */
			const requireChunkId = chunk =>
				`${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${
					fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
				})`;

			// 返回 Promise.all([...]) 加载多个 chunk 的表达式
			return `Promise.all(${comment.trim()}[${chunks
				.map(requireChunkId)
				.join(", ")}])`;
		}

		// 无 chunk 时,返回已完成 Promise
		return `Promise.resolve(${comment.trim()})`;
	}

	/**
	 * 生成异步模块的工厂函数
	 * @param {object} options 配置项
	 * @param {AsyncDependenciesBlock} options.block 异步模块块
	 * @param {ChunkGraph} options.chunkGraph chunk 图
	 * @param {RuntimeRequirements} options.runtimeRequirements 存储运行时代码依赖
	 * @param {string=} options.request 请求路径
	 * @returns {string} 返回模块工厂函数代码
	 */
	asyncModuleFactory({ block, chunkGraph, runtimeRequirements, request }) {
		// 获取该 block 中的第一个依赖
		const dep = block.dependencies[0];
		// 获取该依赖对应的模块
		const module = chunkGraph.moduleGraph.getModule(dep);

		// 生成 ensureChunk 调用代码(可能是 Promise.resolve 或 ensureChunk)
		const ensureChunk = this.blockPromise({
			block,
			message: "",
			chunkGraph,
			runtimeRequirements
		});

		// 生成模块工厂函数 (() => __webpack_require__(moduleId))
		const factory = this.returningFunction(
			this.moduleRaw({
				module,
				chunkGraph,
				request,
				runtimeRequirements
			})
		);

		// 返回最终的异步模块工厂 (() => Promise.then(() => require(id)))
		return this.returningFunction(
			ensureChunk.startsWith("Promise.resolve(")
				? `${factory}`
				: `${ensureChunk}.then(${this.returningFunction(factory)})`
		);
	}

	/**
	 * 生成同步模块的工厂函数
	 * @param {object} options 配置项
	 * @param {Dependency} options.dependency 模块依赖
	 * @param {ChunkGraph} options.chunkGraph chunk 图
	 * @param {RuntimeRequirements} options.runtimeRequirements 存储运行时代码依赖
	 * @param {string=} options.request 请求路径
	 * @returns {string} 返回模块工厂函数代码
	 */
	syncModuleFactory({ dependency, chunkGraph, runtimeRequirements, request }) {
		// 获取该依赖对应的模块
		const module = chunkGraph.moduleGraph.getModule(dependency);

		// 生成模块工厂 (() => __webpack_require__(moduleId))
		const factory = this.returningFunction(
			this.moduleRaw({
				module,
				chunkGraph,
				request,
				runtimeRequirements
			})
		);

		// 再次包一层返回函数(用于构建 factory 的引用)
		return this.returningFunction(factory);
	}

	/**
	 * 返回将 __esModule 标志注入到 exports 对象的语句
	 * @param {object} options 配置项
	 * @param {string} options.exportsArgument exports 对象变量名
	 * @param {RuntimeRequirements} options.runtimeRequirements 存储运行时代码依赖
	 * @returns {string} 代码语句字符串
	 */
	defineEsModuleFlagStatement({ exportsArgument, runtimeRequirements }) {
		// 添加运行时对 __webpack_require__.r 和 exports 的依赖
		runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
		runtimeRequirements.add(RuntimeGlobals.exports);

		// 返回 __webpack_require__.r(exports); 语句
		return `${RuntimeGlobals.makeNamespaceObject}(${exportsArgument});\n`;
	}
}

module.exports = RuntimeTemplate; // 导出 RuntimeTemplate 类
相关推荐
90后的晨仔10 分钟前
解析鸿蒙 ArkTS 中的 Union 类型与 TypeAliases类型
前端·harmonyos
IT_陈寒26 分钟前
Element Plus 2.10.0 重磅发布!新增Splitter组件
前端·人工智能·后端
挑战者66688827 分钟前
vue入门环境搭建及demo运行
前端·javascript·vue.js
贩卖纯净水.27 分钟前
Webpack的基本使用 - babel
前端·webpack·node.js
用户882093216671 小时前
Vue组件通信全攻略:从父子传参到全局状态管理,一篇搞定!
前端
Canmick1 小时前
JavaScript 异步函数健身操
前端·javascript
一曝十寒1 小时前
那些常见的 HTTP 状态码
前端·http
WildBlue1 小时前
🚀 React初体验:从“秃头程序员”到“数据魔法师”的奇幻漂流
前端·react.js
JosieBook1 小时前
【Web应用】若依框架:基础篇14 源码阅读-后端代码分析-课程管理模块前后端代码分析
前端
前端小嘎1 小时前
被大厂裁员后做的前端工具网站
前端