seal
阶段完成后,对于每一个chunk
下的每一个module
都已经生成了相应的代码片段。接下来就需要将这些片段进行拼接,形成可以执行的完整代码,并将代码生成到相应的文件当中。
createChunkAssets
createChunkAssets
会在codeGeneration
回调中调用:
asyncLib.forEachLimit(
this.chunks,
15,
(chunk, callback) => {
manifest = this.getRenderManifest({
chunk,
hash: this.hash,
fullHash: this.fullHash,
outputOptions,
codeGenerationResults: this.codeGenerationResults,
moduleTemplates: this.moduleTemplates,
dependencyTemplates: this.dependencyTemplates,
chunkGraph: this.chunkGraph,
moduleGraph: this.moduleGraph,
runtimeTemplate: this.runtimeTemplate
});
// ...
})
)
首先会对每一个chunk
创建manifest
:
getRenderManifest(options) {
return this.hooks.renderManifest.call([], options);
}
compilation.hooks.renderManifest.tap(
"JavascriptModulesPlugin",
(result, options) => {
// ...
render = () =>
this.renderMain(
{
hash,
chunk,
dependencyTemplates,
runtimeTemplate,
moduleGraph,
chunkGraph,
codeGenerationResults,
strictMode: runtimeTemplate.isModule()
},
hooks,
compilation
);
// ...
result.push({
render,
filenameTemplate,
pathOptions: {
hash,
runtime: chunk.runtime,
chunk,
contentHashType: "javascript"
},
identifier: hotUpdateChunk
? `hotupdatechunk${chunk.id}`
: `chunk${chunk.id}`,
hash: chunk.contentHash.javascript
});
return result;
}
);
此时会进入到JavascriptModulesPlugin
插件当中,对chunk
类型进行检验,并生成带有不同render
的任务,添加到result
当中。完后遍历manifest
,生成文件。精简后的代码如下:
asyncLib.forEach(
manifest,
(fileManifest, callback) => {
// 1. 解析 chunk 的文件等信息
if ("filename" in fileManifest) {
file = fileManifest.filename;
assetInfo = fileManifest.info;
} else {
filenameTemplate = fileManifest.filenameTemplate;
const pathAndInfo = this.getPathWithInfo(
filenameTemplate,
fileManifest.pathOptions
);
file = pathAndInfo.path;
assetInfo = fileManifest.info
? {
...pathAndInfo.info,
...fileManifest.info
}
: pathAndInfo.info;
}
// 2. 生成 chunk 代码
source = fileManifest.render();
// 3. 输出文件
this.emitAsset(file, source, assetInfo);
chunk.files.add(file);
this.hooks.chunkAsset.call(chunk, file);
});
)
主要流程有三部分:首先解析文件的信息,然后调用render
生成代码,最后根据文件信息将生成的代码进行输出。render
函数(renderMain
和renderChunk
)均在webpack/lib/javascript/JavascriptModulesPlugin
插件中定义,其中renderMain
相较于renderChunk
更加复杂,因为它是作为入口文件,通常会加入一些runtime
相关的执行函数等。
renderMain
最终生成的chunk
代码由各个部分组成,包括我们自己写的和引用的模块代码,程序执行的一些runtime
代码,程序启动代码,以及一些其他注释、立即执行等辅助结构代码。
模块代码
const chunkModules = Template.renderChunkModules(
chunkRenderContext,
inlinedModules
? allModules.filter(m => !inlinedModules.has(m))
: allModules,
module => this.renderModule(module, chunkRenderContext, hooks, true),
prefix
);
调用Template.renderChunkModules
函数遍历chunk
中的所有module
,然后将这些module
形成键值对结构代码(实际上是一行一行代码组成的数组结构,但是执行时是对象结构),存放到__webpack_modules__
变量当中。例如:
/***/ "./src/moduleA.js":
/*!************************!*\
!*** ./src/moduleA.js ***!
\************************/
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"A\": () => (/* binding */ A)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n\nconsole.log(react__WEBPACK_IMPORTED_MODULE_0__)\n\nfunction A() {\n console.log('==> module A');\n}\n\n//# sourceURL=webpack://study-webpack/./src/moduleA.js?");
/***/ })
/******/ });