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 类
相关推荐
天天扭码14 分钟前
深入解析 JavaScript 中的每一类函数:从语法到对比,全面掌握适用场景
前端·javascript·面试
小希爸爸34 分钟前
4、中医基础入门和养生
前端·后端
kooboo china.37 分钟前
Tailwind CSS 实战:基于 Kooboo 构建企业官网页面(一)
前端·css·编辑器
uhakadotcom43 分钟前
Fluid:云原生数据加速与管理的简单入门与实战
前端
鬼面瓷1 小时前
CAPL编程_03
前端·数据库·笔记
帅云毅1 小时前
Web漏洞--XSS之订单系统和Shell箱子
前端·笔记·web安全·php·xss
北上ing1 小时前
同一页面下动态加载内容的两种方式:AJAX与iframe
前端·javascript·ajax
小墨宝2 小时前
js 生成pdf 并上传文件
前端·javascript·pdf
HED2 小时前
用扣子快速手撸人生中第一个AI智能应用!
前端·人工智能
DN金猿3 小时前
使用npm install或cnpm install报错解决
前端·npm·node.js