起初,基于webpack构建的图形关系体系,chunks通过父子关系进行链接。过去我们通过CommonsChunkPlugin来避免重复的依赖,但是却达不到更进一步的优化。
webpack4.x中,CommonsChunkPlugin已经被optimization.splitChunks取代,以达到更好的优化体验。
Defaults(默认)
开箱即用SplitChunksPlugin应该能够满足大部分用户的需求。
默认情况下,plugin只影响按需加载的chunk,因为改变初始化的chunk(个人理解为非引入模块(module)的部分)将会改变html中script标签包含的脚本内容。(这个部分个人的理解是splitChunkPlugin只会去对那些引入的模块进行拆分复用,而不会去操作其他的代码)
webpack将根据以下的条件将chunk自动分割并打包出来:(默认规则)
- 被分割出来的chunk必须是共享的某个module,或者说是来自node_modules文件夹中的某个包;
- 被分割出来的chunk在压缩前(webpack打包前)必须大于等于30kb,配置中的单位是字节;
- 按需加载的chunk最终被拆分成的文件数量<=5;
- 入口点最终被拆分成的文件数量<=3;
为了尽可能满足最后两个条件,推荐保证被分割出来的chunk尽量大些。
Configuration(配置)
为了方便开发者进行个性化的配置,plugin提供了一系列的配置。
插件的默认配置提供了web性能最佳实践,但是对于不同的项目,这可能不是最佳策略。如果你要进行个性化配置,则应评估所做更改的影响,以确保确实有好处。
1.optimization.splitChunks
此对象为plugin的配置对象,默认的配置如下:
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
}
2.splitChunks.automaticNameDelimiter
type: String
默认情况下,webpack将使用chunk的来源和名称来生成bundle的名称(比如vendors~main.js)。此选项使你可以指定用于生成名称的定界符(就是vendors~main.js中的~符号)。
3.splitChunks.automaticNameMaxLength
type: Number, 默认值为109
通过这个配置,你可以指定通过plugin生成的bundle文件名的最大长度。
4.splitChunks.chunks
type: function (chunk)、string
这个配置指定了哪些chunk将会被优化。
它可以赋值为一个字符串,这个字符串可以是all、async和initial。
- initial:直接通过import或者require导入的模块将会被split;
- 如:
import a from './a.js' 或 const a = require('./a.js')
- 如:
- async:动态按需导入的模块将会被分割,以import为例,如下所示:
import(/* webpackChunkName: "a" */ './a.js').then(a => {
console.log(a)
})
- all:包含上述两种情况;
或者,如果你想个性化控制,你也可以选择赋值为一个function。返回值将指示是否包括每个块。
module.exports = {
//...
optimization: {
splitChunks: {
chunks (chunk) {
// exclude `my-excluded-chunk` 排除my-excluded-chunk
return chunk.name !== 'my-excluded-chunk';
}
}
}
};
您可以将此配置与HtmlWebpackPlugin结合使用。它将为您注入所有生成的vendor chunk。
注:当入口文件中的module是动态按需导入的,一定会被分割并打包出来。
对于这个配置更加详细的描述,请阅读我的另一篇文章:https://blog.csdn.net/YaoDeBiAn/article/details/103653686
5.splitChunks.maxAsyncRequests
type: Number
用来表示按需加载的模块其能拆分的最大数量;
更加详细的描述可以查阅我的另一篇文章:https://blog.csdn.net/YaoDeBiAn/article/details/104036432
6.splitChunks.maxInitialRequests
type: Number
该配置表示入口点能被拆分的最大数量。
更加详细的描述可以查阅我的另一篇文章:https://blog.csdn.net/YaoDeBiAn/article/details/104087565
7.splitChunks.minChunks
type: Number
split前单个非按需导入的module的并行数最低下限。
更加详细的描述可以查阅我的另一篇文章:https://blog.csdn.net/YaoDeBiAn/article/details/104149392
8.splitChunks.minSize
type: Numbe
所引入的包最小要达到某个大小才能被拆分。
9.splitChunks.maxSize
type: Number
使用maxSize(全局配置:optimization.splitChunks.maxSize,单个缓存组配置:optimization.splitChunks.cacheGroups[x].maxSize,备用缓存组配置:optimization.splitChunks.fallbackCacheGroup.maxSize)告诉webpack尝试将大于maxSize字节的块拆分为较小的部分(大于等于minSize,小于等于maxSize)。该算法是确定性的,对模块的更改只会产生局部影响。这样,在使用长期缓存时就可以使用它并且不需要记录。maxSize小于minSize。
如果不受maxSize影响,拆分的chunk已经有一个名字name。而当我们应用了maxSize时,基于原来chunk拆分出来的bundle,它的名称将基于name进行派生,原理是webpack会基于splitChunks.hidePathInfo生成一个key(基于模块名或者模块的hash派生),这个key会被添加进name中,这个key个人猜测就是之前案例中的~100271bb,它或许是对应的bundle生成的hash值的前几位数字或字符。
maxSize该选项旨在与HTTP / 2和长期缓存一起使用。它增加了请求数量以实现更好的缓存。它还可以用于减小文件大小,以加快重建速度。
maxSize具有比maxInitialRequest/maxAsyncRequests更高的优先权。实际优先级排序为:maxInitialRequest/maxAsyncRequests < maxSize < minSize。
对于minSize和maxSize可以查阅我的另一篇文章:https://blog.csdn.net/YaoDeBiAn/article/details/104123664
10.splitChunks.name
type: boolean = true、 function (module, chunks, cacheGroupKey) => string、string
该配置的值有三种选择,可以是一个布尔值(true和false),也可以是一个函数(形式如function (module, chunks, cacheGroupKey) => string),又或许是一个单纯的String类型。
当然该配置也可以在缓存组中单独配置,如:splitChunks.cacheGroups.{缓存组的名称}.name。
该配置控制被拆分出来的chunk名称。
如果配置为一个布尔值,比如默认下该配置为true,对于生成的chunk的名称,将会基于打包过程中chunks和缓存组名称自动生成。
你可以通过给该配置配置一个字符串或者函数来自定义定制打包后chunk的名称。如果配置的字符串是静态的或者配置的函数返回的是一个静态的字符串,将会使得被另外单独拆分的所有chunk都被打包到一个单独的文件中,这会导致页面首次加载增加,减慢页面的加载。
如果给该配置赋值为一个函数,你会发现参数中的chunk.name和chunk.hash对于定制打包后生成的name非常方便。(这里所说的chunk是参数chunks参数的某一项,chunks是所有chunk的集合)
如果splitChunks.name匹配到一个入口点名称,打包后生成的bundle中该入口点将会被删除。
我们推荐在生产环境下将splitChunks.name配置成false,这将保证不会不必要地更改名称。
main.js
import _ from 'lodash';
console.log(_.join(['Hello', 'webpack'], ' '));
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
// cacheGroupKey here is `commons` as the key of the cacheGroup
name(module, chunks, cacheGroupKey) {
const moduleFileName = module.identifier().split('/').reduceRight(item => item);
const allChunksNames = chunks.map((item) => item.name).join('~');
return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
},
chunks: 'all'
}
}
}
}
}
运行上述的splitChunks配置,webpack 将根据cacheGroup名和chunk名输出一个拆分文件:(以commons-main-lodash.js.e7519d2bb8777058fa27.js散列形式作为真实世界输出的示例)。
在为不同的拆分块分配相同的名称时,这些原本应该被单独打包的module都将放在一个共享的块中,尽管不建议这样做,因为这可能会导致一次性下载的大小增加。
11.splitChunks.cacheGroups
type: Object
缓存组可以继承和/或覆盖splitChunks.*;中的任何选项。但是test、priority以及reuseExistingChunk只能在高速缓存组级别配置。要禁用任何默认缓存组,请将它们设置为false。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
default: false
}
}
}
}
12.splitChunks.cacheGroups.{cacheGroup}.priority
type: Number
一个模块可以属于多个缓存组。优化将首选具有较高的缓存组priority。默认组的优先级为负,以允许自定义组获得更高的优先级(自定义缓存组的默认优先级为0)。
13.splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk
type: Boolean
表示是否使用已有的 chunk,true 则表示如果当前的 chunk 包含的模块已经被抽取出去了,那么将不会重新生成新的,即几个chunk复用被拆分出去的一个module。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
reuseExistingChunk: true
}
}
}
}
}
14.splitChunks.cacheGroups.{cacheGroup}.test
type: function (module, chunk) => boolean、RegExp、string
正则过滤 modules,默认为所有的 modules,可匹配模块路径或 chunk 名字,当匹配到某个 chunk
的名字时,这个chunk里面引入的所有 module 都会选中。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
test(module, chunks) {
//...
return module.type === 'javascript/auto';
}
}
}
}
}
}
15.splitChunks.cacheGroups.{cacheGroup}.filename
type: String
这个属性会覆盖output.filename这个属性,前提是chunks设为initial。同时,这个属性也能全局设置,比如splitChunks.filename,但是如果chunks设为非initial,webpack将会报错,所以官方建议这个属性不全局设置。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
filename: '[name].bundle.js'
}
}
}
}
}
16.splitChunks.cacheGroups.{cacheGroup}.enforce
type: boolean = false
当设为true时,webpack会忽略splitChunks.minSize、splitChunks.minChunks、splitChunks.maxAsyncRequests、splitChunks.maxInitialRequests这几个配置项,并且只要某个缓存组设置了enforce为true,匹配的模块就会忽略前面提到的那几个属性,即使有其他的缓存组匹配同样的模块,也没有设置enforce,同时优先级比设置了enforce的高,enforce: true仍然有效。
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
enforce: true
}
}
}
}
}
last(最后)
非常感谢您能阅读完这篇文章,您的阅读是我不断前进的动力。如果上述内容或许有些错误或者有些个人理解比较偏离实际,还望指出,谢谢!!!对于上面所述,有什么新的观点或发现有什么错误,希望您能指出。
最后,附上个人常逛的社交平台:
知乎:https://www.zhihu.com/people/bi-an-yao-91/activities
csdn:https://blog.csdn.net/YaoDeBiAn
github: https://github.com/yaodebian个人目前能力有限,并没有自主构建一个社区的能力,如有任何问题或想法与我沟通,请通过上述某个平台联系我,谢谢!!!