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?");/***/ })/******/ });
