# 源码解析六

# compilation.seal

上章说到 compilation.seal,这个方法主要是用来生成 chunks,并对 chunks 进行一系列的优化,并生成要输出的代码。 webpack 中的 chunk, 就是对应的为配置在 entry 中的模块,或者是动态引入的模块。

Chunk

如上面,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,结束构建流程。

更新时间: 9/23/2020, 3:25:31 PM