# 源码解析六
# compilation.seal
上章说到 compilation.seal,这个方法主要是用来生成 chunks,并对 chunks 进行一系列的优化,并生成要输出的代码。 webpack 中的 chunk, 就是对应的为配置在 entry 中的模块,或者是动态引入的模块。
如上面,Chunk 的主要属性是 _modules,用来记录包含的所有模块对象。
- 先把 entry 中对应的 module 都生成一个新的 chunk
- 遍历module.dependencies,将其依赖的模块也加入到上一步生成的chunk中
- 若某个module是动态引入的,为其创建一个新的chunk,接着遍历依赖
# createChunkAssets
生成最终的代码,主要在compilation.seal 中调用了 compilation.createChunkAssets方法。
for (let i = 0; i < this.chunks.length; i++) {
const chunk = this.chunks[i];
const template = chunk.hasRuntime()
? this.mainTemplate
: this.chunkTemplate;
const manifest = template.getRenderManifest({
...
})
...
for (const fileManifest of manifest) {
source = fileManifest.render();
}
...
this.emitAsset(file, source, assetInfo);
}
createChunkAssets方法会遍历chunks,来渲染每一个chunk生成代码。其实,compilation对象在实例化时,同时还会实例化三个对象,分别是MainTemplate, ChunkTemplate和ModuleTemplate。这三个对象是用来渲染chunk,得到最终代码模板的。它们之间的不同在于,MainTemplate用来渲染入口 chunk,ChunkTemplate用来渲染非入口 chunk,ModuleTemplate用来渲染 chunk 中的模块。
这里, MainTemplate 和 ChunkTemplate 的 render 方法是用来生成不同的"包装代码"的,MainTemplate 对应的入口 chunk 需要带有 webpack 的启动代码,所以会有一些函数的声明和启动。而包装代码中,每个模块的代码是通过 ModuleTemplate 来渲染的,不过同样只是生成”包装代码”来封装真正的模块代码,而真正的模块代码,是通过模块实例的 source 方法来提供。
最终生成的代码就是我们 源码分析一 里面的代码
每个chunk的源码生成之后,会调用 emitAsset 将其存在 compilation.assets 中。当所有的 chunk 都渲染完成之后,assets 就是最终更要生成的文件列表。至此,compilation 的 seal 方法结束,也代表着 compilation 实例的所有工作到此也全部结束,意味着一次构建过程已经结束,接下来只有文件生成的步骤了。
# emit
在 Compiler 开始生成文件前,钩子 emit 会被执行,这是我们修改最终文件的最后一个机会,生成的在此之后,我们的文件就不能改动了。
this.hooks.emit.callAsync(compilation, err => {
if (err) return callback(err);
outputPath = compilation.getPath(this.outputPath);
this.outputFileSystem.mkdirp(outputPath, emitFiles);
});
webpack 会直接遍历 compilation.assets 生成所有文件,然后触发钩子done,结束构建流程。