本文内容基于webpack 5.74.0
版本进行分析
webpack5核心流程
专栏共有5篇,使用流程图的形式分析了webpack5的构建原理
:
- 「Webpack5源码」make阶段(流程图)分析
- 「Webpack5源码」enhanced-resolve路径解析库源码分析
- 「Webpack5源码」seal阶段(流程图)分析(一)
- 「Webpack5源码」seal阶段分析(二)-SplitChunksPlugin源码
- 「Webpack5源码」seal阶段分析(三)-生成代码&runtime
前言
在上一篇文章「Webpack5源码」seal阶段(流程图)分析(一)中,我们已经分析了seal阶段相关的逻辑,主要包括:
new ChunkGraph()
- 遍历
this.entries
,进行Chunk
和ChunkGroup
的创建 buildChunkGraph()
的整体流程
seal(callback) {
const chunkGraph = new ChunkGraph(
this.moduleGraph,
this.outputOptions.hashFunction
);
this.chunkGraph = chunkGraph;
//...
this.logger.time("create chunks");
/** @type {Map<Entrypoint, Module[]>} */
for (const [name, { dependencies, includeDependencies, options }] of this.entries) {
const chunk = this.addChunk(name);
const entrypoint = new Entrypoint(options);
//...
}
//...
buildChunkGraph(this, chunkGraphInit);
this.logger.timeEnd("create chunks");
this.logger.time("optimize");
//...
while (this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups)) {
/* empty */
}
//...
this.logger.timeEnd("optimize");
this.logger.time("code generation");
this.codeGeneration(err => {
//...
this.logger.timeEnd("code generation");
}
}
buildChunkGraph()
是上篇文章分析中最核心的部分,主要分为3个部分展开
const buildChunkGraph = (compilation, inputEntrypointsAndModules) => {
// PART ONE
logger.time("visitModules");
visitModules(...);
logger.timeEnd("visitModules");
// PART TWO
logger.time("connectChunkGroups");
connectChunkGroups(...);
logger.timeEnd("connectChunkGroups");
for (const [chunkGroup, chunkGroupInfo] of chunkGroupInfoMap) {
for (const chunk of chunkGroup.chunks)
chunk.runtime = mergeRuntime(chunk.runtime, chunkGroupInfo.runtime);
}
// Cleanup work
logger.time("cleanup");
cleanupUnconnectedGroups(compilation, allCreatedChunkGroups);
logger.timeEnd("cleanup");
};
其中visitModules()
整体逻辑如下所示
在上篇文章结束分析buildChunkGraph()
之后,我们将开始hooks.optimizeChunks()
的相关逻辑分析
文章内容
在没有使用SplitChunksPlugin
进行分包优化的情况下,如上图所示,一共会生成6个chunk(4个入口文件形成的chunk,2个异步加载形成的chunk),从上图可以看出,有多个依赖库都被重复打包进入不同的chunk中,对于这种情况,我们可以使用SplitChunksPlugin
进行分包优化,如下图所示,分包出两个新的chunk:test2
和test3
,将重复的依赖都打包进去test2
和test3
,避免重复打包造成的打包文件体积过大的问题
本文以上面例子作为核心,分析SplitChunksPlugin
分包优化的流程
1.hooks.optimizeChunks
while (this.hooks.optimizeChunks.call(this.chunks, this.chunkGroups)) {
/* empty */
}
在经过visitModules()
处理后,会调用hooks.optimizeChunks.call()
进行chunks
的优化,如下图所示,会触发多个Plugin
执行,其中我们最熟悉的就是SplitChunksPlugin
插件,下面会集中SplitChunksPlugin
插件进行讲解
2.SplitChunksPlugin源码解析
配置cacheGroups
,可以对目前已经划分好的chunks
再进行优化,将一个大的chunk
划分为两个及以上的chunk
,减少重复打包,增加代码的复用性
比如入口文件打包形成app1.js
和app2.js
,这两个文件(chunk)存在重复的打包代码:第三方库js-cookie
我们是否能将js-cookie
打包形成一个新的chunk,这样就可以提出app1.js和app2.js里面的第三方库js-cookie
代码,同时只需要一个地方打包js-cookie
代码
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
}
2.0 整体流程图和代码流程概述
2.0.1 代码
根据logger.time进行划分,整个流程主要分为:
prepare
:初始化一些数据结构和方法,为下面流程做准备modules
:遍历所有模块,构建出chunksInfoMap