文章目录
module、chunk和bundle
要理解splitChunks,先要理解webpack的提出的几个概念,module、chunk和bundle。
- module:每个import引入的文件就是一个模块(也就是直接手写的代码)
- chunk:当module源文件传到webpack进行打包时,webpack会根据文件引用关系生成chunk(也就是module在webpack处理时是chunk)
- bundle:是对chunk进行压缩等处理后的产出(也就是打包后可以直接运行的文件)
webapck拆分出的chunk有四种:
- 根据入口文件拆分
- 根据异步文件拆分(import(‘…’))
- 根据spliteChunk拆分
- 还有一些运行时runtimeChunk,负责
- 形如
import('abc').then(res=>{})
这种异步加载的代码,在webpack中即为运行时代码 - 每次更改所谓的运行时代码文件时,打包构建时app.js的hash值是不会改变的。如果每次项目更新都会更改app.js的hash值,那么用户端浏览器每次都需要重新加载变化的app.js,如果项目大切优化分包没做好的话会导致第一次加载很耗时,导致用户体验变差。现在设置了runtimeChunk,就解决了这样的问题。所以
这样做的目的是避免文件的频繁变更导致浏览器缓存失效,所以其是更好的利用缓存。提升用户体验。
- 形如
splitChunks
默认配置:
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
假如我们有这样一个配置
module.exports = {
entry: {
app: './src/index'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[hash:8].bundle.js', // 用于以entry为入口的chunk
chunkFilename: '[name].[hash:16].bundle.js' // 用于动态加载的chunk
}
}
// .src/index.js
const test = 123
console.log(test)
import _ from 'lodash'
setTimeout(()=> {
import(/* webpackChunkName: 'test' */ './test')
import(/* webpackChunkName: 'math' */ './math')
}, 2000)
// ./test.js
import './helper'
// ./math.js
import './helper'
// ./helper.js
// 从jQuery官网download的源码,helper.js Size > 30kb
默认在build的时候,webpack会将lodash和业务逻辑都打包到一个bundle中,这个bundle非常的大
splitChunks.chunks
async,表明只能分离异步的模块
- app.js下的bundle包含了lodash模块,而math和test两个文件由于share了同一个helper,并且这个helper的size大于30kb,因此被分离出来,成为一个新的bundle
chunks:initial
当为initial时,只能分离初始化的模块,异步的模块无法分离
- app.js里的lodash被分离成一个bundle,而math和test里的js并没有分离出来,没有处理异步模块内的内容
chunks:all
将初始化或者异步的模块都分离出来
- lodash和helper都被分离成单个bundle了
当splitChunks.chunk为一个函数时,可以自行选择哪些chunk需要被分离
optimization: {
splitChunks: {
chunks (chunk) {
return chunk.name !== 'test' && chunk.name !== 'math'
}
}
}
- lodash被分离成单个bundle,而helper并没有被分离出来
splitChunks.maxAsyncRequests
异步文件的最大并行请求数量
optimization: {
splitChunks: {
maxAsyncRequests: 1 // 2
}
}
在build的时候会发现,这时候webpack并没有帮我们做codeSplitting
将maxAsyncRequests改成2时,helper里的内容就会分离成一个单独的bundle。
splitChunks.maxInitialRequests
entry的最大并行请求数,默认是3个。这个选项主要适用于多页应用的项目
maxInitialRequest、maxAsyncRequests 、maxSize 、 minSize的优先级
maxInitialRequest/maxAsyncRequests < maxSize < minSize
.
抽离案例
Vendors
chunk-a: react, react-dom, 一些组件1
chunk-b: react, react-dom, 一些组件2
chunk-c: angular, 一些组件3
chunk-d: angular, 一些组件4
webpack4 会自动创建两个vendor chunks,结果如下:
vendors~chunk-a~chunk-b: react, react-dom
vendors~chunk-c~chunk-d: angular
chunk-a 到 chunk-d: 只包含各自的组件
重复的vendors
chunk-a: react, react-dom, 一些组件1
chunk-b: react, react-dom, lodash, 一些组件2
chunk-c: react, react-dom, lodash, 一些组件3
webpack4同样也会创建两个vendors chunks,结果如下:
vendors~chunk-a~chunk-b~chunk-c: react, react-dom
vendors~chunk-b~chunk-c: lodash
chunk-a 到 chunk-c: 只只包含各自的组件
共享的modules
chunk-a: vue, 一些组件1, 一些被共享的业务组件(非node_modules资源)
chunk-b: vue, 另一些组件2, 一些被共享的业务组件
chunk-c: vue, 另一些组件3, 一些被共享的业务组件
假定被共享的组件大小超过30kb,那么webpack4会创建一个venrdors chunk和一个commons chunk,结果如下:
vendors~chunk-a~chunk-b~chunk-c: vue
comons-chunk-a~chunk-b~chunk-c: 一些被共享的业务组件
chunk-a 到 chunk-c: 只包含各自的组件
当被共享的业务组件大小小于30kb时,webpack4会将他们重复的模块放到chunk-a到chunk-c中,而不是分离出一个common chunk来。我们认为,下载一个体积小于30kb的chunk而导致额外的http请求,是不值得的。
多个分享的模块
chunk-a: react, react-dom, 一些组件1, 一些共享的react组件
chunk-b: react, react-dom, angular, 一些组件2
chunk-c: react, react-dom, angular, 一些组件3, 一些共享的react组件, 一些共享的angular组件
chunk-d: angular, 一些组件4, 一些共享的angular组件
webpack4会创建两个vendors chunks和两个commons chunks
vendors~chunk-a~chunk-b~chunk-c: react, react-dom
vendors~chunk-b~chunk-c~chunk-d: angular
commons~chunk-a~chunk-c: 一些共享的react组件
commons~chunk-c~chunk-d: 一些共享的angular组件
chunk-a to chunk-d: 各自的组件
配置解析
-
chunks:可用值为“all”、“async”、“initial”,默认值是“async”
- all:拆分同步和异步文件
- async:只对异步文件作处理
- initial:只对入口文件进行拆分,不去处理异步文件里的模块
-
minSize:控制最小包的大小,大于这个值才会去拆分,如果拆分的公共模块小于这个大小,那就复制成多份,直接打包到引用该模块的包里
-
minChunks:模块的重复调用次数大于等于minChunks值时,就会满足这项拆包条件,但只看入口模块导入的,不看动态加载模块中导入的(import(‘…’)),即使设置的chunks为“all”。
-
maxInitialRequests:入口文件最大请求的文件数量(import等方式)
- 入口文件本身算一个请求
- 入口文件动态加载的模块不算在内
- 通过runtimeChunk拆分出来的runtime文件不算在内
- 只算js,css不算在内
- 如果同时有两个模块满足cacheGroups的拆分规则,但maxInitialRequests只允许再拆分一个,那么会拆出体积更大的那个模块。
-
maxAsyncRequests:用于限制异步模块内部的并行最大请求数
- import文件本身算一个请求
- 只算js、css不算在内
- 如果同时有两个模块满足cacheGroups的规则需要拆分,但maxAsyncRequests只允许拆分一个时,那么会拆出体积更大的那个模块。
-
name:主要用于分离chunks后的名字,可以是字符串或者函数,相同name会合并成一个chunk
-
splitChunks.cacheGroups
- 可以继承或者重写splitChunks对象下的属性,但是test、priority和reuseExistingChunk只能配置在cacheGroup对象中
- 每个添加的对象都有默认配置,如果想禁用此配置,可以将其设置为false