Skip to content

createChunkAssets

对于这个函数我们需要先了解一下 webpack 的一个属性 output

output 位于对象最顶级键(key),包括了一组选项,指示 webpack 如何去输出、以及在哪里输出你的「bundle、asset 和其他你所打包或使用 webpack 载入的任何内容」。

特别是其中两个值 filename 和 chunkFilename。这两个决定了输出的文件的入口起点的文件名,chunkFilename 决定了非入口(non-entry) chunk 文件的名称。分别对应上一步 seal chunk 图生产中 入口 chunk 和其依赖的异步 blockChunk。

js
let createChunkAssets = () => {
	const outputOptions = this.outputOptions;
	const cachedSourceMap = new Map();
	const alreadyWrittenFiles = new Map();
	for (let i = 0; i < this.chunks.length; i++) {
		const chunk = this.chunks[i];
		chunk.files = [];
		let source;
		let file;
		let filenameTemplate;
		try {
			const template = chunk.hasRuntime() ? this.mainTemplate : this.chunkTemplate;
			const manifest = template.getRenderManifest({
				chunk,
				hash: this.hash,
				fullHash: this.fullHash,
				outputOptions,
				moduleTemplates: this.moduleTemplates,
				dependencyTemplates: this.dependencyTemplates,
			}); // [{ render(), filenameTemplate, pathOptions, identifier, hash }]
			for (const fileManifest of manifest) {
				const cacheName = fileManifest.identifier;
				const usedHash = fileManifest.hash;
				filenameTemplate = fileManifest.filenameTemplate;
				const pathAndInfo = this.getPathWithInfo(filenameTemplate, fileManifest.pathOptions);
				file = pathAndInfo.path;
				const assetInfo = pathAndInfo.info;

				// check if the same filename was already written by another chunk
				const alreadyWritten = alreadyWrittenFiles.get(file);
				if (alreadyWritten !== undefined) {
					if (alreadyWritten.hash === usedHash) {
						if (this.cache) {
							this.cache[cacheName] = {
								hash: usedHash,
								source: alreadyWritten.source,
							};
						}
						chunk.files.push(file);
						this.hooks.chunkAsset.call(chunk, file);
						continue;
					} else {
						throw new Error(
							`Conflict: Multiple chunks emit assets to the same filename ${file}` +
								` (chunks ${alreadyWritten.chunk.id} and ${chunk.id})`
						);
					}
				}
				if (this.cache && this.cache[cacheName] && this.cache[cacheName].hash === usedHash) {
					source = this.cache[cacheName].source;
				} else {
					source = fileManifest.render();
					// Ensure that source is a cached source to avoid additional cost because of repeated access
					if (!(source instanceof CachedSource)) {
						const cacheEntry = cachedSourceMap.get(source);
						if (cacheEntry) {
							source = cacheEntry;
						} else {
							const cachedSource = new CachedSource(source);
							cachedSourceMap.set(source, cachedSource);
							source = cachedSource;
						}
					}
					if (this.cache) {
						this.cache[cacheName] = {
							hash: usedHash,
							source,
						};
					}
				}
				this.emitAsset(file, source, assetInfo);
				chunk.files.push(file);
				this.hooks.chunkAsset.call(chunk, file);
				alreadyWrittenFiles.set(file, {
					hash: usedHash,
					source,
					chunk,
				});
			}
		} catch (err) {
			this.errors.push(new ChunkRenderError(chunk, file || filenameTemplate, err));
		}
	}
};
  1. const template = chunk.hasRuntime() ? this.mainTemplate : this.chunkTemplate;

这是根据 chunk 是入口 chunk 还是依赖的异步 blockChunk,来决定生成文件的 template

  1. 获取 render 对象
js
const manifest = template.getRenderManifest({
	chunk,
	hash: this.hash,
	fullHash: this.fullHash,
	outputOptions,
	moduleTemplates: this.moduleTemplates,
	dependencyTemplates: this.dependencyTemplates,
});

这一步

  1. 生成文件的路径
js
// filenameTemplate 文件名称的模板 如 * , [name].[hash:8].js
// fileManifest.pathOptions 在步骤2中处理后的文件路径相关信息
const pathAndInfo = this.getPathWithInfo(filenameTemplate, fileManifest.pathOptions);

在这一步也是调用了很多钩子函数。通过处理后的文件路径信息然后调用相应模板的 getAssetPathWithInfo() 去处理资源路径问题。

在主 mainTemplate 的 getAssetPathWithInfo 中是通过 mainTemplate.hooks.assetPath钩子函数去调用在 WebpackOptionsApply.process() 定义的 TemplatedPathPlugin 插件。

下面我们看这个插件的

js
mainTemplate.hooks.assetPath.tap('TemplatedPathPlugin', replacePathVariables);

/**
 * 替换路径中的变量
 * @param  {[type]} path      [description]
 * @param  {[type]} data      [description]
 * @param  {[type]} assetInfo [description]
 * @return {[type]}           [description]
 */
const replacePathVariables = (path, data, assetInfo) => {
	const chunk = data.chunk;
	// chunk 的id
	const chunkId = chunk && chunk.id;
	// chunk的名称用来替换 name
	const chunkName = chunk && (chunk.name || chunk.id);
	const chunkHash = chunk && (chunk.renderedHash || chunk.hash);
	const chunkHashWithLength = chunk && chunk.hashWithLength;
	const contentHashType = data.contentHashType;
	const contentHash = (chunk && chunk.contentHash && chunk.contentHash[contentHashType]) || data.contentHash;
	const contentHashWithLength =
		(chunk && chunk.contentHashWithLength && chunk.contentHashWithLength[contentHashType]) ||
		data.contentHashWithLength;
	const module = data.module;
	const moduleId = module && module.id;
	const moduleHash = module && (module.renderedHash || module.hash);
	const moduleHashWithLength = module && module.hashWithLength;

	if (typeof path === 'function') {
		path = path(data);
	}

	if (data.noChunkHash && (REGEXP_CHUNKHASH_FOR_TEST.test(path) || REGEXP_CONTENTHASH_FOR_TEST.test(path))) {
		throw new Error(`Cannot use [chunkhash] or [contenthash] for chunk in '${path}' (use [hash] instead)`);
	}

	return (
		path
			// 替换路径中的 [hash] [hash:8]
			.replace(REGEXP_HASH, withHashLength(getReplacer(data.hash), data.hashWithLength, assetInfo))
			// 替换路径中的 [chunkhash] [chunkhash:8]
			.replace(REGEXP_CHUNKHASH, withHashLength(getReplacer(chunkHash), chunkHashWithLength, assetInfo))
			// 替换路径中的 [contenthash] [contenthash:8]
			.replace(REGEXP_CONTENTHASH, withHashLength(getReplacer(contentHash), contentHashWithLength, assetInfo))
			// 替换路径中的 [modulehash] [modulehash:8]
			.replace(REGEXP_MODULEHASH, withHashLength(getReplacer(moduleHash), moduleHashWithLength, assetInfo))
			// 替换路径中的 [id] 以chunkId作为值
			.replace(REGEXP_ID, getReplacer(chunkId))
			// 替换路径中的 [moduleid] 以moduleId作为值
			.replace(REGEXP_MODULEID, getReplacer(moduleId))
			// 替换路径中的 [name] 以chunkName作为值
			.replace(REGEXP_NAME, getReplacer(chunkName))
			// 替换路径中的 [file] 以filename作为值
			.replace(REGEXP_FILE, getReplacer(data.filename))
			// 替换路径中的 [filebase] 以basename作为值
			.replace(REGEXP_FILEBASE, getReplacer(data.basename))
			// query is optional, it's OK if it's in a path but there's nothing to replace it with
			// // 替换路径中的 [query] 以chunk的query值
			.replace(REGEXP_QUERY, getReplacer(data.query, true))
			// only available in sourceMappingURLComment
			// 替换路径中的 [url] 以url作为值
			.replace(REGEXP_URL, getReplacer(data.url))

			.replace(/\[\\(\\*[\w:]+\\*)\\\]/gi, '[$1]')
	);
};

下面我们需要分别说明一下这几个值是怎么获取的

  • hash

  • chunkhash

  • contenthash

  • modulehash

  • id

模块标识符(module identifier)

  • moduleid

  • name

  • file

  • filebase

  • query

  • url

js
getPathWithInfo(filename, data) {
			data = data || {};
			data.hash = data.hash || this.hash;
			return this.mainTemplate.getAssetPathWithInfo(filename, data);
		}

4. render

异步 block chunk 模块

首先我们看一份简单的结果

js
(window['webpackJsonp'] = window['webpackJsonp'] || []).push([
	[1],
	{
		/***/ './resolve/src/modules/asyncModule.js':
			/*!********************************************!*\
  !*** ./resolve/src/modules/asyncModule.js ***!
  \********************************************/
			/*! exports provided: default */
			/***/ function(module, __webpack_exports__, __webpack_require__) {
				'use strict';
				eval(
					'__webpack_require__.r(__webpack_exports__);\nlet moduleName = \'asyncModule\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\tmoduleName: moduleName,\n\tfunc() {\n\t\tconsole.log(moduleName);\n\t}\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL2FzeW5jTW9kdWxlLmpzLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL3Jlc29sdmUvc3JjL21vZHVsZXMvYXN5bmNNb2R1bGUuanM/ZDQzOCJdLCJzb3VyY2VzQ29udGVudCI6WyJsZXQgbW9kdWxlTmFtZSA9ICdhc3luY01vZHVsZSc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcblx0bW9kdWxlTmFtZTogbW9kdWxlTmFtZSxcclxuXHRmdW5jKCkge1xyXG5cdFx0Y29uc29sZS5sb2cobW9kdWxlTmFtZSk7XHJcblx0fSxcclxufTtcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBSkEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/modules/asyncModule.js\n'
				);

				/***/
			},
	},
]);
js
source = fileManifest.render();

这一步主要做以下:

  1. 处理 chunk 中依赖的模块代码

renderChunkModules()

这个方法的主要流程是处理模块中所有的同步和异步模块,然后对于同步的模块经过 Template.renderChunkModules() -> chunkTemplate.hooks.modules.call() -> chunkTemplate.hooks.render.call() -> chunkTemplate.hooks.renderWithEntry.call(),然后在最后将所有的模块作为一个对象的值,其 key 为 module 的 id 处理。

这样其结构就变成如下:

js
// 还是有一些其他的注释
{
  "./resolve/src/modules/asyncModule1.js" :
  /*!*********************************************!
   *!*** ./resolve/src/modules/asyncModule1.js ***!
   *********************************************/
  (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    eval("具体的代码 //# sourceURL=[module]\n//# sourceMappingURL=")
  })
}

简要代码如下

js
	static renderChunkModules(
		chunk,
		filterFn,
		moduleTemplate,
		dependencyTemplates,
		prefix = ""
	) {

    // 处理当前chunk中所有的模块将其变成可以执行的代码
		const allModules = modules.map(module => {
			return {
				id: module.id,
				source: moduleTemplate.render(module, dependencyTemplates, {
					chunk
				})
			};
		});

		// 将所有的处理后的模块代码作为值,module.id为键变成一个对象
			source.add("{\n");
			allModules.sort(stringifyIdSortPredicate).forEach((module, idx) => {
				if (idx !== 0) {
					source.add(",\n");
				}
				source.add(`\n/***/ ${JSON.stringify(module.id)}:\n`);
				source.add(module.source);
			});
			source.add(`\n\n${prefix}}`);

		return source;
	}

处理每一个的模块的代码如下:

js
renderJavascript(chunkTemplate, chunk, moduleTemplate, dependencyTemplates) {
  // 生成子chunk的代码
	const moduleSources = Template.renderChunkModules(
		chunk,
		m => typeof m.source === "function",
		moduleTemplate,
		dependencyTemplates
	);
	const core = chunkTemplate.hooks.modules.call(
		moduleSources,
		chunk,
		moduleTemplate,
		dependencyTemplates
	);
	let source = chunkTemplate.hooks.render.call(
		core,
		chunk,
		moduleTemplate,
		dependencyTemplates
	);
	if (chunk.hasEntryModule()) {
		source = chunkTemplate.hooks.renderWithEntry.call(source, chunk);
	}
	chunk.rendered = true;
	return new ConcatSource(source, ";");
}
  • 处理模块中的依赖和导出

module.source( dependencyTemplates, this.runtimeTemplate, this.type )

这一步很重要主要是操作模块的内容的,如处理模块中的依赖和导出

import common from '../common/common'; 转变成 "/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./resolve/src/common/common.js");"

  • 生成 module 的 sourcemap 内容和模块调用代码

const moduleSourcePostModule = this.hooks.module.call( moduleSourcePostContent, module, options, dependencyTemplates );

最后结果就变成这个样子了

如上图所示,其生成当前模块的 sourcemap 代码

  • 将处理后的代码变成一个函数,在加载后自动执行

this.hooks.render.call( moduleSourcePostModule, module, options, dependencyTemplates )

在 module 中只是生成了 module 的内容代码,下面需要再给其添加 调用的代码,如下图:

可见其给每一个模块添加了一个(function(module, __webpack_exports__, __webpack_require__){ ... })

  • 添加模块化注释代码

this.hooks.package.call( moduleSourcePostRender, module, options, dependencyTemplates )

继续操作上面的代码,在这一步主要就是添加模块化的注释代码,表明这个模块原来的路径是什么

可见其给每一个模块添加了一个./resolve/src/modules/asyncModule1_1.js的注释

  1. 添加异步模块加载的代码

let source = chunkTemplate.hooks.render.call( core, chunk, moduleTemplate, dependencyTemplates );

这一步是添加 jsonp 的代码

这样其结构就变成如下:

js
(window['webpackJsonp'] = window['webpackJsonp'] || []).push([
	[`${chunk.ids}`],
	{
		模块的代码,
	},
]);

上面这是对于异步模块 chunk 的处理方式,还有对于入口模块的处理方式

入口模块

对于入口模块其处理方式跟上面不一样,首先其使用的是mainTemplate去处理入口 chunk,然后其经历一下几个过程

js
render(hash, chunk, moduleTemplate, dependencyTemplates) {
		const buf = this.renderBootstrap(
			hash,
			chunk,
			moduleTemplate,
			dependencyTemplates
		);
		let source = this.hooks.render.call(
			new OriginalSource(
				Template.prefix(buf, " \t") + "\n",
				"webpack/bootstrap"
			),
			chunk,
			hash,
			moduleTemplate,
			dependencyTemplates
		);
		if (chunk.hasEntryModule()) {
			source = this.hooks.renderWithEntry.call(source, chunk, hash);
		}
		if (!source) {
			throw new Error(
				"Compiler error: MainTemplate plugin 'render' should return something"
			);
		}
		chunk.rendered = true;
		return new ConcatSource(source, ";");
	}
  1. 其中renderBootstrap是主要的过程,在这个方法中其生成启动代码、异步模块初始化加载和 prefetch 延迟加载、module 缓存等代码,大体如下:
js
let render = (hash, chunk, moduleTemplate, dependencyTemplates) => {
  // 生成入口模块的启动代码
	const buf = this.renderBootstrap(hash, chunk, moduleTemplate, dependencyTemplates);

};

/**
 * TODO webpack 5: remove moduleTemplate and dependencyTemplates
 * @param {string} hash hash to be used for render call
 * @param {Chunk} chunk Chunk instance
 * @param {ModuleTemplate} moduleTemplate ModuleTemplate instance for render
 * @param {Map<Function, DependencyTemplate>} dependencyTemplates dependency templates
 * @returns {string[]} the generated source of the bootstrap code
 */
renderBootstrap(hash, chunk, moduleTemplate, dependencyTemplates) {
	const buf = [];
	buf.push(
		this.hooks.bootstrap.call(
			"",
			chunk,
			hash,
			moduleTemplate,
			dependencyTemplates
		)
	);
	buf.push(this.hooks.localVars.call("", chunk, hash));
	buf.push("");
	buf.push("// The require function");
	buf.push(`function ${this.requireFn}(moduleId) {`);
	buf.push(Template.indent(this.hooks.require.call("", chunk, hash)));
	buf.push("}");
	buf.push("");
	buf.push(
		Template.asString(this.hooks.requireExtensions.call("", chunk, hash))
	);
	buf.push("");
	buf.push(Template.asString(this.hooks.beforeStartup.call("", chunk, hash)));
	const afterStartupCode = Template.asString(
		this.hooks.afterStartup.call("", chunk, hash)
	);
	if (afterStartupCode) {
		// TODO webpack 5: this is a bit hacky to avoid a breaking change
		// change it to a better way
		buf.push("var startupResult = (function() {");
	}
	buf.push(Template.asString(this.hooks.startup.call("", chunk, hash)));
	if (afterStartupCode) {
		buf.push("})();");
		buf.push(afterStartupCode);
		buf.push("return startupResult;");
	}
	return buf;
}

具体通过以下过程:

  • jsonp 结果回调方法

buf.push( this.hooks.bootstrap.call( "", chunk, hash, moduleTemplate, dependencyTemplates ) );

这一部分处理的是生成一下代码

js
/******/ // install a JSONP callback for chunk loading
/******/ function webpackJsonpCallback(data) {
	/******/ var chunkIds = data[0];
	/******/ var moreModules = data[1]; // add "moreModules" to the modules object, // then flag all "chunkIds" as loaded and fire callback
	/******/
	/******/
	/******/ /******/ /******/ var moduleId,
		chunkId,
		i = 0,
		resolves = [];
	/******/ for (; i < chunkIds.length; i++) {
		/******/ chunkId = chunkIds[i];
		/******/ if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
			/******/ resolves.push(installedChunks[chunkId][0]);
			/******/
		}
		/******/ installedChunks[chunkId] = 0;
		/******/
	}
	/******/ for (moduleId in moreModules) {
		/******/ if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
			/******/ modules[moduleId] = moreModules[moduleId];
			/******/
		}
		/******/
	}
	/******/ if (parentJsonpFunction) parentJsonpFunction(data);
	/******/
	/******/ while (resolves.length) {
		/******/ resolves.shift()();
		/******/
	}
	/******/
	/******/
} // The module cache
  • 模块一些变量的处理,如 installedModules、installedCssChunks

this.hooks.localVars.call("", chunk, hash)

这一步有多个钩子函数分别为

this.hooks.localVars.tap("MainTemplate", (source, chunk, hash) => {})

这个钩子函数代码甚少就是添加模块缓存

js
this.hooks.localVars.tap('MainTemplate', (source, chunk, hash) => {
	return Template.asString([source, '// The module cache', 'var installedModules = {};']);
});

mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {})

这个钩子函数是 mini-css-extra-plugin 添加的,主要是添加加载的 css 的缓存

js
mainTemplate.hooks.localVars.tap(pluginName, (source, chunk) => {
	const chunkMap = this.getCssChunkObject(chunk);
	if (Object.keys(chunkMap).length > 0) {
		return Template.asString([
			source,
			'',
			'// object to store loaded CSS chunks',
			'var installedCssChunks = {',
			Template.indent(chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n')),
			'}',
		]);
	}
	return source;
});

mainTemplate.hooks.localVars.tap( "JsonpMainTemplatePlugin", (source, chunk, hash) => {})

这个钩子函数是 JsonpMainTemplatePlugin 添加的,主要是处理入口模块中异步 block chunks 的,分为初始化就加载的 chunk 其存放在 installedChunks,延迟加载的,存放在deferredModules,同时生成异步加载 chunkd 的路径 path 的函数 jsonpScriptSrc

js
mainTemplate.hooks.localVars.tap('JsonpMainTemplatePlugin', (source, chunk, hash) => {
	const extraCode = [];
	// 是否存在异步block chunk,
	if (needChunkLoadingCode(chunk)) {
		extraCode.push(
			'',
			'// object to store loaded and loading chunks',
			'// undefined = chunk not loaded, null = chunk preloaded/prefetched',
			'// Promise = chunk loading, 0 = chunk loaded',
			'var installedChunks = {',
			Template.indent(chunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n')),
			'};',
			'',
			needEntryDeferringCode(chunk)
				? needPrefetchingCode(chunk)
					? 'var deferredModules = [], deferredPrefetch = [];'
					: 'var deferredModules = [];'
				: ''
		);
	}
	// 生成异步加载的chunk的路径获取函数
	if (needChunkOnDemandLoadingCode(chunk)) {
		extraCode.push(
			'',
			'// script path function',
			'function jsonpScriptSrc(chunkId) {',
			Template.indent([`return ${mainTemplate.requireFn}.p + ${getScriptSrcPath(hash, chunk, 'chunkId')}`]),
			'}'
		);
	}
	if (extraCode.length === 0) return source;
	return Template.asString([source, ...extraCode]);
});

通过上面的代码在入口 chunk 中又添加了一下的代码

mainTemplate.hooks.localVars.tap( "WasmMainTemplatePlugin", (source, chunk) => {})

这个钩子函数是 WasmMainTemplatePlugin 添加的,主要是处理二进制模块的,暂时不做阅读

  • this.hooks.require.tap("MainTemplate", (source, chunk, hash) => {})

最后我们看一下通过 renderBootstrap 具体生成的结果

  1. this.hooks.render.call()

在这个钩子函数中我们也找到一个事件 MainTemplate,然后我们看这个钩子函数,其主要做两件事:

  • 将 renderBootstrap 通过一个函数包裹 (function(modules) { renderBootstrap内容 })( module内容 )

  • 通过MainTemplate.hooks.modules.call(new RawSource(''), chunk, hash, moduleTemplate, dependencyTemplates)生成入口 chunk 依赖的同步 module 的内容,具体可以参考前面的 renderChunkModules的流程,这边也是通过 Template.js 去处理入口 chunk 依赖的同步 module,然后作为前面 function 的参数。

源码如下:

js
this.hooks.render.tap('MainTemplate', (bootstrapSource, chunk, hash, moduleTemplate, dependencyTemplates) => {
	const source = new ConcatSource();
	source.add('/******/ (function(modules) { // webpackBootstrap\n');
	source.add(new PrefixSource('/******/', bootstrapSource));
	source.add('/******/ })\n');
	source.add('/************************************************************************/\n');
	source.add('/******/ (');
	// 生成同步module的依赖对象 如 {}
	source.add(this.hooks.modules.call(new RawSource(''), chunk, hash, moduleTemplate, dependencyTemplates));
	source.add(')');
	return source;
});
  1. 装换成 ConcatSource 实例对象

最后 将 chunk 标记为已 render(chunk.rendered = true), 然后将 render 的结果作为参数生成 ConcatSource 的实例对象

总结

对于入口 EntryModule,其跟依赖的 blockchunk 的处理方式有异有同,分别如下:

不同点:

  1. 加载方式
  • 对于 blockChunk,其不是直接添加到 html 中而是通过入口 EntryModule 去决定其初始化加载还是可能 prefetch 加载,所以需要在入口 EntryModule 生成加载异步 blockchunk 的方法(**webpack_require.e**)

  • 对于入口 EntryModule,其 chunk.hasEntryModule 所以可以在 html-webpack-plugin 中去判断直接插入到 html 中

  1. 对于同步 module 的加载方式不一样
  • 在 blockChunk 中其将所有的模块存放在 window['webpackJsonp'], 然后在入口 module 中修改window['webpackJsonp'].push方法,使得在执行异步 blockChunk 的时候将异步 blockChunk 的 module 通过 webpackJsonpCallback()添加到 modules 中

  • 对于入口 EntryModule 中依赖的通过 modules,其直接作为参数 modules 的值((function(modules) {})({ module1 , module2 ...}))

相同点

  • 同时通过 Template.js 去处理依赖的同步 module,并将其变成一个 key 为module.id,值为 module 的内容的对象属性
js
/******/ (function(modules) {
	// webpackBootstrap
	/******/ // install a JSONP callback for chunk loading
	/******/ function webpackJsonpCallback(data) {
		/******/ var chunkIds = data[0];
		/******/ var moreModules = data[1]; // add "moreModules" to the modules object, // then flag all "chunkIds" as loaded and fire callback
		/******/
		/******/
		/******/ /******/ /******/ var moduleId,
			chunkId,
			i = 0,
			resolves = [];
		/******/ for (; i < chunkIds.length; i++) {
			/******/ chunkId = chunkIds[i];
			/******/ if (Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
				/******/ resolves.push(installedChunks[chunkId][0]);
				/******/
			}
			/******/ installedChunks[chunkId] = 0;
			/******/
		}
		/******/ for (moduleId in moreModules) {
			/******/ if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
				/******/ modules[moduleId] = moreModules[moduleId];
				/******/
			}
			/******/
		}
		/******/ if (parentJsonpFunction) parentJsonpFunction(data);
		/******/
		/******/ while (resolves.length) {
			/******/ resolves.shift()();
			/******/
		}
		/******/
		/******/
	} // The module cache
	/******/
	/******/
	/******/ /******/ var installedModules = {}; // object to store loaded and loading chunks // undefined = chunk not loaded, null = chunk preloaded/prefetched // Promise = chunk loading, 0 = chunk loaded
	/******/
	/******/ /******/ /******/ /******/ var installedChunks = {
		/******/ main: 0,
		/******/
	}; // script path function
	/******/
	/******/
	/******/
	/******/ /******/ function jsonpScriptSrc(chunkId) {
		/******/ return __webpack_require__.p + '' + ({}[chunkId] || chunkId) + '.js';
		/******/
	} // The require function
	/******/
	/******/ /******/ function __webpack_require__(moduleId) {
		/******/
		/******/ // Check if module is in cache
		/******/ if (installedModules[moduleId]) {
			/******/ return installedModules[moduleId].exports;
			/******/
		} // Create a new module (and put it into the cache)
		/******/ /******/ var module = (installedModules[moduleId] = {
			/******/ i: moduleId,
			/******/ l: false,
			/******/ exports: {},
			/******/
		}); // Execute the module function
		/******/
		/******/ /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // Flag the module as loaded
		/******/
		/******/ /******/ module.l = true; // Return the exports of the module
		/******/
		/******/ /******/ return module.exports;
		/******/
	} // This file contains only the entry chunk. // The chunk loading function for additional chunks
	/******/
	/******/ /******/ /******/ __webpack_require__.e = function requireEnsure(chunkId) {
		/******/ var promises = []; /******/ // JSONP chunk loading for javascript
		/******/
		/******/
		/******/ /******/ var installedChunkData = installedChunks[chunkId];
		/******/ if (installedChunkData !== 0) {
			// 0 means "already installed".
			/******/
			/******/ // a Promise means "currently loading".
			/******/ if (installedChunkData) {
				/******/ promises.push(installedChunkData[2]);
				/******/
			} else {
				/******/ // setup Promise in chunk cache
				/******/ var promise = new Promise(function(resolve, reject) {
					/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject];
					/******/
				});
				/******/ promises.push((installedChunkData[2] = promise)); // start chunk loading
				/******/
				/******/ /******/ var script = document.createElement('script');
				/******/ var onScriptComplete;
				/******/
				/******/ script.charset = 'utf-8';
				/******/ script.timeout = 120;
				/******/ if (__webpack_require__.nc) {
					/******/ script.setAttribute('nonce', __webpack_require__.nc);
					/******/
				}
				/******/ script.src = jsonpScriptSrc(chunkId); // create error before stack unwound to get useful stacktrace later
				/******/
				/******/ /******/ var error = new Error();
				/******/ onScriptComplete = function(event) {
					/******/ // avoid mem leaks in IE.
					/******/ script.onerror = script.onload = null;
					/******/ clearTimeout(timeout);
					/******/ var chunk = installedChunks[chunkId];
					/******/ if (chunk !== 0) {
						/******/ if (chunk) {
							/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type);
							/******/ var realSrc = event && event.target && event.target.src;
							/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
							/******/ error.name = 'ChunkLoadError';
							/******/ error.type = errorType;
							/******/ error.request = realSrc;
							/******/ chunk[1](error);
							/******/
						}
						/******/ installedChunks[chunkId] = undefined;
						/******/
					}
					/******/
				};
				/******/ var timeout = setTimeout(function() {
					/******/ onScriptComplete({ type: 'timeout', target: script });
					/******/
				}, 120000);
				/******/ script.onerror = script.onload = onScriptComplete;
				/******/ document.head.appendChild(script);
				/******/
			}
			/******/
		}
		/******/ return Promise.all(promises);
		/******/
	}; // expose the modules object (__webpack_modules__)
	/******/
	/******/ /******/ __webpack_require__.m = modules; // expose the module cache
	/******/
	/******/ /******/ __webpack_require__.c = installedModules; // define getter function for harmony exports
	/******/
	/******/ /******/ __webpack_require__.d = function(exports, name, getter) {
		/******/ if (!__webpack_require__.o(exports, name)) {
			/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
			/******/
		}
		/******/
	}; // define __esModule on exports
	/******/
	/******/ /******/ __webpack_require__.r = function(exports) {
		/******/ if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
			/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
			/******/
		}
		/******/ Object.defineProperty(exports, '__esModule', { value: true });
		/******/
	}; // create a fake namespace object // mode & 1: value is a module id, require it // mode & 2: merge all properties of value into the ns // mode & 4: return value when already ns object // mode & 8|1: behave like require
	/******/
	/******/ /******/ /******/ /******/ /******/ /******/ __webpack_require__.t = function(value, mode) {
		/******/ if (mode & 1) value = __webpack_require__(value);
		/******/ if (mode & 8) return value;
		/******/ if (mode & 4 && typeof value === 'object' && value && value.__esModule) return value;
		/******/ var ns = Object.create(null);
		/******/ __webpack_require__.r(ns);
		/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
		/******/ if (mode & 2 && typeof value != 'string')
			for (var key in value)
				__webpack_require__.d(
					ns,
					key,
					function(key) {
						return value[key];
					}.bind(null, key)
				);
		/******/ return ns;
		/******/
	}; // getDefaultExport function for compatibility with non-harmony modules
	/******/
	/******/ /******/ __webpack_require__.n = function(module) {
		/******/ var getter =
			module && module.__esModule
				? /******/ function getDefault() {
						return module['default'];
				  }
				: /******/ function getModuleExports() {
						return module;
				  };
		/******/ __webpack_require__.d(getter, 'a', getter);
		/******/ return getter;
		/******/
	}; // Object.prototype.hasOwnProperty.call
	/******/
	/******/ /******/ __webpack_require__.o = function(object, property) {
		return Object.prototype.hasOwnProperty.call(object, property);
	}; // __webpack_public_path__
	/******/
	/******/ /******/ __webpack_require__.p = '/'; // on error function for async loading
	/******/
	/******/ /******/ __webpack_require__.oe = function(err) {
		console.error(err);
		throw err;
	};
	/******/
	/******/ var jsonpArray = (window['webpackJsonp'] = window['webpackJsonp'] || []);
	/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
	/******/ jsonpArray.push = webpackJsonpCallback;
	/******/ jsonpArray = jsonpArray.slice();
	/******/ for (var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
	/******/ var parentJsonpFunction = oldJsonpFunction; // Load entry module and return exports
	/******/
	/******/
	/******/ /******/ return __webpack_require__((__webpack_require__.s = './resolve/src/main.js'));
	/******/
})(
	/************************************************************************/
	/******/ {
		/***/ './resolve/src/common/common.js':
			/*!**************************************!*\
  !*** ./resolve/src/common/common.js ***!
  \**************************************/
			/*! exports provided: default */
			/***/ function(module, __webpack_exports__, __webpack_require__) {
				'use strict';
				eval(
					'__webpack_require__.r(__webpack_exports__);\nconst a = \'123\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\ta\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9jb21tb24vY29tbW9uLmpzLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL3Jlc29sdmUvc3JjL2NvbW1vbi9jb21tb24uanM/ZGQ3ZSJdLCJzb3VyY2VzQ29udGVudCI6WyJjb25zdCBhID0gJzEyMyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcblx0YSxcclxufTtcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBREEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/common/common.js\n'
				);

				/***/
			},

		/***/ './resolve/src/css/css.css':
			/*!*********************************!*\
  !*** ./resolve/src/css/css.css ***!
  \*********************************/
			/*! no static exports found */
			/***/ function(module, exports, __webpack_require__) {
				eval(
					'// extracted by mini-css-extract-plugin//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9jc3MvY3NzLmNzcy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy8uL3Jlc29sdmUvc3JjL2Nzcy9jc3MuY3NzPzVhMGUiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gZXh0cmFjdGVkIGJ5IG1pbmktY3NzLWV4dHJhY3QtcGx1Z2luIl0sIm1hcHBpbmdzIjoiQUFBQSIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./resolve/src/css/css.css\n'
				);

				/***/
			},

		/***/ './resolve/src/main.js':
			/*!*****************************!*\
  !*** ./resolve/src/main.js ***!
  \*****************************/
			/*! no exports provided */
			/***/ function(module, __webpack_exports__, __webpack_require__) {
				'use strict';
				eval(
					'__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _modules_module1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./modules/module1 */ "./resolve/src/modules/module1.js");\n/* harmony import */ var _modules_module2__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @/modules/module2 */ "./resolve/src/modules/module2.js");\n/* harmony import */ var _css_css_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @/css/css.css */ "./resolve/src/css/css.css");\n/* harmony import */ var _css_css_css__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_css_css_css__WEBPACK_IMPORTED_MODULE_2__);\n\n\n\nvar name = "development" + \'-ele\';\n\nconst module3 = __webpack_require__(/*! ./modules/module3 */ "./resolve/src/modules/module3.js");\n\nconst moduleName = \'main\';\n\nObject(_modules_module2__WEBPACK_IMPORTED_MODULE_1__["func"])();\n\n\n\nconst asyncModule = resolve => __webpack_require__.e(/*! AMD require */ 1).then(function() { var __WEBPACK_AMD_REQUIRE_ARRAY__ = [__webpack_require__(/*! @/modules/asyncModule */ "./resolve/src/modules/asyncModule.js")]; (resolve).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);}.bind(this)).catch(__webpack_require__.oe);\n\nconst asyncModule1 = resolve => __webpack_require__.e(/*! AMD require */ 2).then(function() { var __WEBPACK_AMD_REQUIRE_ARRAY__ = [__webpack_require__(/*! @/modules/asyncModule1 */ "./resolve/src/modules/asyncModule1.js")]; (resolve).apply(null, __WEBPACK_AMD_REQUIRE_ARRAY__);}.bind(this)).catch(__webpack_require__.oe);\n\nconsole.log(module3);\nconsole.log(asyncModule);\nconsole.log(asyncModule1);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tYWluLmpzLmpzIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vL3Jlc29sdmUvc3JjL21haW4uanM/YTk4ZSJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgbW9kdWxlMSBmcm9tICcuL21vZHVsZXMvbW9kdWxlMSc7XHJcbmltcG9ydCB7IGZ1bmMgfSBmcm9tICdAL21vZHVsZXMvbW9kdWxlMic7XHJcblxyXG52YXIgbmFtZSA9IHByb2Nlc3MuZW52Lk5PREVfRU5WICsgJy1lbGUnO1xyXG5cclxuY29uc3QgbW9kdWxlMyA9IHJlcXVpcmUoJy4vbW9kdWxlcy9tb2R1bGUzJyk7XHJcblxyXG5jb25zdCBtb2R1bGVOYW1lID0gJ21haW4nO1xyXG5cclxuZnVuYygpO1xyXG5cclxuaW1wb3J0ICdAL2Nzcy9jc3MuY3NzJztcclxuXHJcbmNvbnN0IGFzeW5jTW9kdWxlID0gcmVzb2x2ZSA9PiByZXF1aXJlKC8qIHdlYnBhY2tDaHVua05hbWU6IFwiYXN5bmNNb2R1bGVcIiAqLyBbJ0AvbW9kdWxlcy9hc3luY01vZHVsZSddLCByZXNvbHZlKTtcclxuXHJcbmNvbnN0IGFzeW5jTW9kdWxlMSA9IHJlc29sdmUgPT4gcmVxdWlyZSgvKiB3ZWJwYWNrQ2h1bmtOYW1lOiBcImFzeW5jTW9kdWxlMVwiICovIFsnQC9tb2R1bGVzL2FzeW5jTW9kdWxlMSddLCByZXNvbHZlKTtcclxuXHJcbmNvbnNvbGUubG9nKG1vZHVsZTMpO1xyXG5jb25zb2xlLmxvZyhhc3luY01vZHVsZSk7XHJcbmNvbnNvbGUubG9nKGFzeW5jTW9kdWxlMSk7XHJcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/main.js\n'
				);

				/***/
			},

		/***/ './resolve/src/modules/module1.js':
			/*!****************************************!*\
  !*** ./resolve/src/modules/module1.js ***!
  \****************************************/
			/*! exports provided: default */
			/***/ function(module, __webpack_exports__, __webpack_require__) {
				'use strict';
				eval(
					'__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _common_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../common/common */ "./resolve/src/common/common.js");\n\n\nlet moduleName = \'module1\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\tmoduleName: moduleName,\n\tfunc() {\n\t\tconsole.log(moduleName);\n\t},\n\tcommon: _common_common__WEBPACK_IMPORTED_MODULE_0__["default"]\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTEuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vcmVzb2x2ZS9zcmMvbW9kdWxlcy9tb2R1bGUxLmpzP2RmMjUiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNvbW1vbiBmcm9tICcuLi9jb21tb24vY29tbW9uJztcclxuXHJcbmxldCBtb2R1bGVOYW1lID0gJ21vZHVsZTEnO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG5cdG1vZHVsZU5hbWU6IG1vZHVsZU5hbWUsXHJcblx0ZnVuYygpIHtcclxuXHRcdGNvbnNvbGUubG9nKG1vZHVsZU5hbWUpO1xyXG5cdH0sXHJcblx0Y29tbW9uXHJcbn07XHJcbiJdLCJtYXBwaW5ncyI6IkFBQUE7QUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBTEEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/modules/module1.js\n'
				);

				/***/
			},

		/***/ './resolve/src/modules/module2.js':
			/*!****************************************!*\
  !*** ./resolve/src/modules/module2.js ***!
  \****************************************/
			/*! exports provided: func, moduleName */
			/***/ function(module, __webpack_exports__, __webpack_require__) {
				'use strict';
				eval(
					'__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "func", function() { return func; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "moduleName", function() { return moduleName; });\n/* harmony import */ var _module2_1__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./module2_1 */ "./resolve/src/modules/module2_1.js");\n\n\nconsole.log(_module2_1__WEBPACK_IMPORTED_MODULE_0__["default"]);\nconst func = () => {\n\tconsole.log(moduleName);\n};\n\nconst moduleName = \'module2\';//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTIuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vcmVzb2x2ZS9zcmMvbW9kdWxlcy9tb2R1bGUyLmpzP2QzMjQiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IG1vZHVsZTJfMSBmcm9tICcuL21vZHVsZTJfMSc7XHJcblxyXG5jb25zb2xlLmxvZyhtb2R1bGUyXzEpO1xyXG5leHBvcnQgY29uc3QgZnVuYyA9ICgpID0+IHtcclxuXHRjb25zb2xlLmxvZyhtb2R1bGVOYW1lKTtcclxufTtcclxuXHJcbmV4cG9ydCBjb25zdCBtb2R1bGVOYW1lID0gJ21vZHVsZTInO1xyXG4iXSwibWFwcGluZ3MiOiJBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSIsInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./resolve/src/modules/module2.js\n'
				);

				/***/
			},

		/***/ './resolve/src/modules/module2_1.js':
			/*!******************************************!*\
  !*** ./resolve/src/modules/module2_1.js ***!
  \******************************************/
			/*! exports provided: func, moduleName */
			/***/ function(module, __webpack_exports__, __webpack_require__) {
				'use strict';
				eval(
					'__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "func", function() { return func; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "moduleName", function() { return moduleName; });\nconst func = () => {\n\tconsole.log(moduleName);\n};\n\nconst moduleName = \'module2_1\';//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTJfMS5qcy5qcyIsInNvdXJjZXMiOlsid2VicGFjazovLy9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTJfMS5qcz9kMDg1Il0sInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCBjb25zdCBmdW5jID0gKCkgPT4ge1xyXG5cdGNvbnNvbGUubG9nKG1vZHVsZU5hbWUpO1xyXG59O1xyXG5cclxuZXhwb3J0IGNvbnN0IG1vZHVsZU5hbWUgPSAnbW9kdWxlMl8xJztcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQUE7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./resolve/src/modules/module2_1.js\n'
				);

				/***/
			},

		/***/ './resolve/src/modules/module3.js':
			/*!****************************************!*\
  !*** ./resolve/src/modules/module3.js ***!
  \****************************************/
			/*! exports provided: default */
			/***/ function(module, __webpack_exports__, __webpack_require__) {
				'use strict';
				eval(
					'__webpack_require__.r(__webpack_exports__);\nlet moduleName = \'module3\';\n\n/* harmony default export */ __webpack_exports__["default"] = ({\n\tmoduleName: moduleName,\n\tfunc() {\n\t\tconsole.log(moduleName);\n\t}\n});//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9yZXNvbHZlL3NyYy9tb2R1bGVzL21vZHVsZTMuanMuanMiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vcmVzb2x2ZS9zcmMvbW9kdWxlcy9tb2R1bGUzLmpzPzNkZmEiXSwic291cmNlc0NvbnRlbnQiOlsibGV0IG1vZHVsZU5hbWUgPSAnbW9kdWxlMyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCB7XHJcblx0bW9kdWxlTmFtZTogbW9kdWxlTmFtZSxcclxuXHRmdW5jKCkge1xyXG5cdFx0Y29uc29sZS5sb2cobW9kdWxlTmFtZSk7XHJcblx0fSxcclxufTtcclxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBSkEiLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///./resolve/src/modules/module3.js\n'
				);

				/***/
			},

		/******/
	}
);

5. emitAsset()

在 render 中通过 mainTemplate 或者 chunkTemplate 等去生成了各个 chunk 的 source 内容,最后存放在 ConcatSource 实例中,然后又通过 CachedSource、cachedSourceMap、complation.cache 三个去缓存生成的结果。然后进入到 this.emitAsset(file, source, assetInfo);中。

emitAsset()作用只是去通过生成的 asset 的 file 名称去缓存 source 的内容

6. this.hooks.chunkAsset.call(chunk, file);

没有了...