本文所有的内容均基于
webpack@4.42.0
所撰写。阅读本文之前需了解Webpack
的一些基本概念,如:entry
、chunk
、module
等。
代码分割是 Webpack
最引人注目的特性之一。这个特性允许开发者将代码分割成不同的包,然后可以按需加载或并行加载这些包。它可以用来实现更小的包,并控制资源加载优先级,如果使用正确,将对加载时间产生重大影响。
分割策略
首先介绍一下 Webpack
默认的代码分割策略。
webpack will automatically split chunks based on these conditions:
- New chunk can be shared OR modules are from the node_modules folder
- New chunk would be bigger than 30kb (before min+gz)
- Maximum number of parallel requests when loading chunks on demand would be lower or equal to 5
- Maximum number of parallel requests at initial page load would be lower or equal to 3
上面这段话来自 Webpack
的官方文档,说的是 Webpack
会在满足下面的条件时自动进行 chunk 的分割 (ps:如果没有特殊配置的话,只有异步加载的 chunk 会自动进行分割)。
- 新的
chunk
可以被共享或者chunk
里的module
来自于node_modules
文件夹 - 新的
chunk
文件大小大于 30kb(压缩前) - 按需加载的
chunk
内部的最大并行请求数不超过 5 个 - 初始化页面的并行请求的最大数量不超过 3 个
满足上面的四个条件 Webpack
就认为可以进行代码分割,前两个条件应该比较好理解,这里解释下后两个条件的意思吧:
按需加载的 chunk
内部的最大并行请求数不超过 5 个
简单来说就是加载 chunk
所发送的请求不能超过 5 个。举个例子:
a.js
依赖了 1.js
、2.js
、3.js
、4.js
这四个文件,且这四个文件满足了代码分割的前两个条件 (被共享且大于 30kb)。这个时候就会分割出五个 chunk
,分别是:a.chunk.js
、1.chunk.js
、2.chunk.js
、3.chunk.js
、4.chunk.js
。此时加载 chunk~a
的并发请求数就恰好是 5 个,编译后的加载代码大致如下:
// t.e 是加载的 chunk 的方法,t.e 的参数应该是 chunk 的 id,为了容易阅读,我这里用了 chunk 的名称替代。
Promise.all([t.e('1.chunk'),t.e('2.chunk'),t.e('3.chunk'),t.e('4.chunk'),t.e('a.chunk')]).then(() => // do some thing)
如果 a.js
又依赖了 5.js
,,这个时候并不会分割出 5.chunk
,因为如果分割出 5.chunk
,那么加载 a.chunk
的请求就是 6
个了, 5.js
的内容会被合并到 a.chunk
中。
初始化页面的并行请求的最大数量不超过 3 个
这里的初始化页面可以理解为加载 entry
,换句话说就是加载 entry
的并行请求的最大数量不超过 3 个,现在就比较好理解了,和上一个条件基本一致,只不过一个是针对 entry
的,一个是针对普通的 chunk
的。这个配置是为了对代码分割的 chunk
数量进行一定的限制,避免分割出太多chunk
导致请求数量过多的情况。
要注意的是:
- 这两个限制并不包括
js
以外的请求比如css
。 - 如果只能允许再拆分一个模块,那尺寸更大的模块会被拆分出来。
分割配置
上面介绍的都是 Webpack
代码分割的默认策略,这些策略是 Webpack
团队认为的最佳实践。
如果你在项目有特殊的需求,比如认为默认的 30kb 太大了,你想超过 10kb 就进行分割。Webpack
也提供了我们一些配置去修改分割策略,这就是 Webpack
中的 optimization.splitChunks
配置,下面就介绍几个比较相对重要的配置,完整的配置可以查看文档。
splitChunks.chunks
function (chunk) | string
chunks
配置的是要针对哪些 chunk
进行代码分割,之前有提到过,默认只会分割异步的加载的 chunk
。chunks
的值为 string
时,有效的值为all
, async
和initial
。
module.exports = {
//...
optimization: {
splitChunks: {
// 分割所有类型的 chunk
chunks: 'all'
}
}
};
module.exports = {
//...
optimization: {
splitChunks: {
chunks (chunk) {
// 除了 `my-excluded-chunk` 都进行分割
return chunk.name !== 'my-excluded-chunk';
}
}
}
};
splitChunks.minChunks
number
minChunks
指的是在代码分割之前 module
至少被几个 chunk
共享。默认值为1
。
splitChunks.minSize
number
minSize
对应的就是分割策略中的第二个条件中的 30kb
的限制,开发者可以根据项目的实际情况灵活调节它的大小。
splitChunks.maxSize
number
这个配置是设置分割出来的 chunk
的最大文件大小的,默认值为 0
,也就是没有上限。设置了该属性,Webpack
就会尽可能地把 chunk
的大小限制在maxSize
的值之内,不过一个完整的module
的代码没法分割成几段,所以该超过还是会超过的。
你可以通过设置这个属性来分割出更多、更小的 chunk
,不过随之而来的就是更多的加载 chunk
的请求。
splitChunks.maxAsyncRequests
number
maxAsyncRequests
对应的是分割策略中的第三条的配置,默认值为 5
splitChunks.maxInitialRequests
number
maxInitialRequests
对应的是分割策略中的第四条的配置,默认值为 3
splitChunks.cacheGroups
cacheGroups
是 splitChunks
中最核心的配置之一,它的配置项包括 splitChunks.*
下的所有配置,且还多了test
、priority
、reuseExistingChunk
三个配置项。
cacheGroups
中的配置会覆盖 splitChunks
中的配置,而且有优先级之分(通过 priority
设置优先级),cacheGroups
主要的用途是针对不同的 chunk
设置不同的分割策略,这可以大大提升代码分割的灵活性,笔者在这里就不详细展开说明使用方法了,感兴趣的可以参考官方文档。