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


moduleNamespace 函数

用于生成访问模块命名空间的表达式(同步)。

  • module:要访问的模块对象,类型为 Module
  • chunkGraph:Webpack 内部的 chunkGraph 实例,用于获取模块 ID 等信息
  • request:模块的请求路径,用于注释和调试信息
  • strict(可选):是否处于 strict ESM 模式,影响导出类型判断
  • weak(可选):是否为弱依赖,若模块 ID 不存在则生成错误代码而非抛出异常
  • runtimeRequirements(可选):传入后,会被填充所需的运行时代码(如 __webpack_require__, __webpack_require__.n, 等)
  • 返回值:一个用于访问该模块命名空间的 JS 表达式字符串

moduleNamespacePromise 函数

用于生成异步访问模块命名空间的 Promise 表达式。

  • chunkGraph:Webpack 的 ChunkGraph 实例
  • block(可选):异步依赖块(如 import()),类型为 AsyncDependenciesBlock
  • module:目标模块对象,类型为 Module
  • request:模块请求路径,主要用于生成注释
  • message:用于生成注释的信息,提示加载内容
  • strict(可选):是否为 strict 模式,影响导出类型处理
  • weak(可选):是否为弱依赖,决定是否在模块 ID 缺失时生成警告代码
  • runtimeRequirements(可选):用于收集运行时依赖
  • 返回值 :一个 Promise 表达式,最终 .then() 得到模块命名空间对象
js 复制代码
/**
 * 根据模块信息生成访问该模块命名空间对象的表达式字符串。
 *
 * @param {Object} options 配置项
 * @param {Module} options.module 当前依赖的模块
 * @param {ChunkGraph} options.chunkGraph ChunkGraph 实例,用于获取模块 ID 等信息
 * @param {string} options.request 当前模块请求路径,用于生成注释
 * @param {boolean=} options.strict 是否开启 strict ESM 模式(影响 exportsType 判断)
 * @param {boolean=} options.weak 是否是弱依赖(模块可能不存在)
 * @param {RuntimeRequirements=} options.runtimeRequirements 若传入,则会被填充依赖的运行时变量
 * @returns {string} 表达式字符串,用于访问模块命名空间(如 require(), __webpack_require__, fakeNamespace 等)
 */
moduleNamespace({
	module,
	chunkGraph,
	request,
	strict,
	weak,
	runtimeRequirements
}) {
	// 模块不存在时,返回缺失模块的表达式
	if (!module) {
		return this.missingModule({ request });
	}

	// 模块存在但未被分配 moduleId
	if (chunkGraph.getModuleId(module) === null) {
		if (weak) {
			// 弱依赖可以容忍没有 id,返回一个可报错表达式
			return this.weakError({
				module,
				chunkGraph,
				request,
				type: "expression"
			});
		}
		// 非弱依赖没有 id 说明是构建错误
		throw new Error(
			`RuntimeTemplate.moduleNamespace(): ${noModuleIdErrorMessage(
				module,
				chunkGraph
			)}`
		);
	}

	// 获取模块 ID 的表达式
	const moduleId = this.moduleId({
		module,
		chunkGraph,
		request,
		weak
	});

	// 获取模块的导出类型(例如 default-only, namespace 等)
	const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);

	switch (exportsType) {
		case "namespace":
			// 直接返回模块原始对象(即命名空间对象)
			return this.moduleRaw({
				module,
				chunkGraph,
				request,
				weak,
				runtimeRequirements
			});
		case "default-with-named":
			// 模块既有默认导出也有命名导出,构造 fake 命名空间,标志为 3
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 3)`;
		case "default-only":
			// 只有默认导出,标志为 1
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 1)`;
		case "dynamic":
			// 动态导出(运行时决定),标志为 7
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 7)`;
	}
}
/**
 * 生成异步访问模块命名空间对象的 Promise 表达式。
 *
 * @param {Object} options 配置项
 * @param {ChunkGraph} options.chunkGraph ChunkGraph 实例
 * @param {AsyncDependenciesBlock=} options.block 当前模块所在的异步依赖块
 * @param {Module} options.module 需要异步加载的模块
 * @param {string} options.request 请求字符串,用于注释
 * @param {string} options.message 异步模块加载时用于注释的提示信息
 * @param {boolean=} options.strict 是否为 strict 模式,影响模块 exportsType 判断
 * @param {boolean=} options.weak 是否为弱依赖(不强制要求模块存在)
 * @param {RuntimeRequirements=} options.runtimeRequirements 若传入,则会被填充依赖的运行时变量
 * @returns {string} 返回一个 Promise 表达式,最终 `.then` 得到模块的命名空间对象
 */
moduleNamespacePromise({
	chunkGraph,
	block,
	module,
	request,
	message,
	strict,
	weak,
	runtimeRequirements
}) {
	// 模块不存在,返回缺失模块的 Promise 表达式
	if (!module) {
		return this.missingModulePromise({ request });
	}

	// 获取模块 ID
	const moduleId = chunkGraph.getModuleId(module);
	if (moduleId === null) {
		if (weak) {
			// 弱依赖可以容忍没有 id,返回运行时报错表达式
			return this.weakError({
				module,
				chunkGraph,
				request,
				type: "promise"
			});
		}
		// 非弱依赖没有 id 抛出异常
		throw new Error(
			`RuntimeTemplate.moduleNamespacePromise(): ${noModuleIdErrorMessage(
				module,
				chunkGraph
			)}`
		);
	}

	// 获取异步加载该模块的 Promise 表达式(如 import())
	const promise = this.blockPromise({
		chunkGraph,
		block,
		message,
		runtimeRequirements
	});

	let appending; // 用于拼接的 .then 链
	let idExpr = JSON.stringify(moduleId); // moduleId 的 JSON 表达形式
	const comment = this.comment({ request }); // 注释字符串
	let header = ""; // 弱依赖时的前置 ID 检查逻辑代码

	if (weak) {
		// 如果 id 表达式太长则使用临时变量
		if (idExpr.length > 8) {
			header += `var id = ${idExpr}; `;
			idExpr = "id";
		}
		// 添加对 Runtime.moduleFactories 的依赖
		runtimeRequirements.add(RuntimeGlobals.moduleFactories);
		// 添加工厂存在性检查逻辑
		header += `if(!${
			RuntimeGlobals.moduleFactories
		}[${idExpr}]) { ${this.weakError({
			module,
			chunkGraph,
			request,
			idExpr,
			type: "statements"
		})} } `;
	}

	// 获取模块 ID 的表达式
	const moduleIdExpr = this.moduleId({
		module,
		chunkGraph,
		request,
		weak
	});

	// 获取模块导出类型
	const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);
	let fakeType = 16; // 标记为 async fake namespace 对象

	switch (exportsType) {
		case "namespace":
			if (header) {
				// 有弱依赖检查逻辑,生成包裹的 .then
				const rawModule = this.moduleRaw({
					module,
					chunkGraph,
					request,
					weak,
					runtimeRequirements
				});
				appending = `.then(${this.basicFunction(
					"",
					`${header}return ${rawModule};`
				)})`;
			} else {
				// 否则直接 require 加载
				runtimeRequirements.add(RuntimeGlobals.require);
				appending = `.then(${RuntimeGlobals.require}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}))`;
			}
			break;

		case "dynamic":
			fakeType |= 4;
		// fallthrough
		case "default-with-named":
			fakeType |= 2;
		// fallthrough
		case "default-only":
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			if (chunkGraph.moduleGraph.isAsync(module)) {
				// 异步模块需要两层 then
				if (header) {
					const rawModule = this.moduleRaw({
						module,
						chunkGraph,
						request,
						weak,
						runtimeRequirements
					});
					appending = `.then(${this.basicFunction(
						"",
						`${header}return ${rawModule};`
					)})`;
				} else {
					runtimeRequirements.add(RuntimeGlobals.require);
					appending = `.then(${RuntimeGlobals.require}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}))`;
				}
				appending += `.then(${this.returningFunction(
					`${RuntimeGlobals.createFakeNamespaceObject}(m, ${fakeType})`,
					"m"
				)})`;
			} else {
				// 同步模块一层 then 即可
				fakeType |= 1;
				if (header) {
					const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, ${fakeType})`;
					appending = `.then(${this.basicFunction(
						"",
						`${header}return ${returnExpression};`
					)})`;
				} else {
					appending = `.then(${RuntimeGlobals.createFakeNamespaceObject}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}, ${fakeType}))`;
				}
			}
			break;
	}

	// 返回最终组合好的 Promise 表达式
	return `${promise || "Promise.resolve()"}${appending}`;
}
相关推荐
gerrgwg29 分钟前
React Hooks入门
前端·javascript·react.js
ObjectX前端实验室30 分钟前
【react18原理探究实践】调度机制之注册任务
前端·react.js
汉字萌萌哒1 小时前
【 HTML基础知识】
前端·javascript·windows
ObjectX前端实验室1 小时前
【React 原理探究实践】root.render 干了啥?——深入 render 函数
前端·react.js
北城以北88883 小时前
Vue--Vue基础(二)
前端·javascript·vue.js
ObjectX前端实验室3 小时前
【react18原理探究实践】更新调度的完整流程
前端·react.js
tanxiaomi4 小时前
通过HTML演示JVM的垃圾回收-新生代与老年代
前端·jvm·html
palpitation974 小时前
Android App Links 配置
前端
FuckPatience4 小时前
Vue 组件定义模板,集合v-for生成界面
前端·javascript·vue.js
sophie旭4 小时前
一道面试题,开始性能优化之旅(3)-- DNS查询+TCP(三)
前端·面试·性能优化