代码分割(Code Splitting)的理念
首先搞明白: webpack里的代码分割是个什么鬼? 它允许你将一个文件分割成多个文件。其原因是基于浏览器会缓存你的代码这一事实。每当你对某一文件做点改变,访问你站点的人们就要重新下载它,包括依赖。如果你将(这些依赖)分离成单独的文件,访问者就无需多次重复下载它们了。
使用webpack生成一个或多个包含你源代码最终版本的“打包好的文件”(bundles),(概念上我们当作)它们由(一个一个的)chunks组成。
作用
下面就开始介绍splitChunks的内容。
配置项
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {}
}
}
}
复制代码
- chunks: 表示哪些代码需要优化,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为async
- minSize: 表示在压缩前的最小模块大小,默认为30000
- minChunks: 表示被引用次数,默认为1
- maxAsyncRequests: 按需加载时候最大的并行请求数,默认为5
- maxInitialRequests: 一个入口最大的并行请求数,默认为3
- automaticNameDelimiter: 命名连接符
- name: 拆分出来块的名字,默认由块名和hash值自动生成
- cacheGroups: 缓存组。缓存组的属性除上面所有属性外,还有test, priority, reuseExistingChunk
- test: 用于控制哪些模块被这个缓存组匹配到
- priority: 缓存组打包的先后优先级
- reuseExistingChunk: 如果当前代码块包含的模块已经有了,就不在产生一个新的代码块
默认情况
首先webpack会根据下述条件自动进行代码块分割:
- 新代码块可以被共享引用,或者这些模块都是来自node_modules文件夹里面
- 新代码块大于30kb(min+gziped之前的体积)
- 按需加载的代码块,并行请求最大数量应该小于或者等于5
- 初始加载的代码块,并行请求最大数量应该小于或等于3
块打包默认情况下只会影响按需加载模块,因为对初始块也进行优化打包会影响HTML中的script标签数,增加请求数。
例如:
模块全部是同步引入
// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
console.log(_.join(['a', 'b'], '~'))
ReactDOM.render(
<div>SplitChunkdiv>,
document.getElementById('root')
)
复制代码
默认情况只会影响按需加载模块,所以所有内容全部被打包到一起了。
有模块动态导入
// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import(/* webpackChunkName: "async-jquery" */ 'jquery').then(component => {
console.log(component)
})
console.log(_.join(['a', 'b'], '~'))
ReactDOM.render(
<div>SplitChunkdiv>,
document.getElementById('root')
)
复制代码
这里jquery使用动态导入,打包结果中可以看到jquery被单独打包了
chunks
chunks的取值是有initial, async, all。默认情况下是async
注意点:
initial
模式下会把异步和非异步模块分开打包。而 all
会把异步和非异步同时进行打包。也就是说moduleA在indexA中异步引入,indexB中同步引入, initial
下moduleA会出现在两个打包块中,而 all
只会出现一个。
cacheGroups
使用cacheGroups可以自定义配置打包块。
// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import $ from 'jquery'
// indexB.js
import React from 'react'
import ReactDOM from 'react-dom'
import('lodash')
import $ from 'jquery'
// webpack.config.js
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2
}
}
}
}
复制代码
根据开头介绍webapck分割条件,一些公共模块被打包进了commons,自定义打包块的优先级是0,所以现在公共模块会被打包进commons,而不是上述提到的默认打包块vendors(优先级为负)。
但是这边为什么lodash为什么没打包在一起呢?可以回顾下initial和all的区别。接下来实验下all的效果。
// indexA, indexB同上
// webpack.config.js
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'all',
minChunks: 2
}
}
}
}
复制代码
结果在预期中,lodash被打包在一起了。
提取第三方库
最后看下之前CommonsChunkPlugin常用的分离部分第三方库功能。这边你可以想一下怎么操作。
上面已经提到了设置 chunks: initial || all
都可以提取出第三方库。但是它是把所有第三库提取出来,所以我们在只提取react和react-dom的情况下,需要自定义一个cacheGroup。
// indexA.js
import React from 'react'
import ReactDOM from 'react-dom'
import _ from 'lodash'
import $ from 'jquery'
// webpack.config.js
entry: {
indexA: path.join(__dirname, 'src/indexA.js')
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /react/,
name: 'vendors'
}
}
}
}
复制代码
我们去重写了vendors打包块,只打包匹配react的模块,所以达到了之前CommonsChunkPlugin的功能。
最后
runtimeChunk: { name: 'runtime'},
//等价于
runtimeChunk:'runtime'
复制代码