Webpack-代码切割提取

avatar

Webpack SplitChunks 代码切割与提取

前文写过 Webpack 的优化配置,本文细说其中的 SplitChunks 配置

chunks,module,bundle

module:在js中,每一个导入导出的模块,称为module,比如

1
2
module.export = {}
export default {}

引入的文件或库,插件等

chunks:指webpack中的配置splitChunks.chunks,打包出来的入口文件:

1
2
3
4
5
6
splitChunks: {
chunks: 'all',
cacheGroups: {
****
}
}

bundle:由webpack最终拆分出来的每一个包,如
chunks

为什么需要拆分包

原因在于,随着项目开发后期时可能会引入第三方包,或者本地业务代码,工具库越来越大,首屏加载速度或某页面加载速度缓慢,单个文件可能达到几M甚至几十M的可能

如何配置

首先说下splitChunks下有哪些配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
optimization:{
splitChunks:{
usedExports: '', // 找出模块使用哪些导出来处理导出名称,忽略未使用的导出并生成更有效的代码。如果是true:分析每个运行时使用的导出,则是"global":全局分析所有运行时的导出合并。
chunks: '', // 'all','async','initial', 'all':异步与非异步都进入该选项匹配中,'async':只有异步模块进入,'initial':初始化时就能获取的模块
minSize: '', // 生成块的最小大小(以字节为单位)
minRemainingSize: '', // 在webpack 5中引入了option选项,通过确保拆分后剩余的最小块大小超过限制来避免大小为零的模块。默认为0,平时少用到
maxSize: '', // 最大模块拆分大小
minChunks: '', // entry引用的次数最小值才会被抽离出去
maxAsyncRequests: '', // 按需加载时最大并行请求数
maxInitialRequests: '', // 入口点的最大并行请求数
automaticNameDelimiter: '', // 默认情况下,webpack将使用块的来源和名称生成名称(例如vendors~main.js)。此选项使您可以指定用于生成名称的定界符。
enforceSizeThreshold: '', // 强制执行拆分的大小阈值和其他限制(minRemainingSize,maxAsyncRequests,maxInitialRequests)将被忽略。
// 核心配置,对代码的拆分规则全在cacheGroups缓存组里配置,缓存组的每一个属性都是一个配置规则,我这里给他的default属性进行了配置,属性名可以不叫default可以自己定。属性的值是一个对象,里面放的我们对一个代码拆分规则的描述。
cacheGroups: [
{
name: '', // 提取出来的公共模块将会以这个来命名
chunks: '', // 指定哪些类型的chunk参与拆分,值可以是string可以是函数
priority: '', // cacheGroups中存在多个cache配置,该配置为当前规则的权重,越大匹配优先级越高
}
],
}
}

cacheGroups外的配置为splitChunks的全局配置,每一个cacheGroups中配置都会使用外部的配置

例子

假设项目文件夹为

1
2
3
4
5
6
7
8
9
|--public/
| |--index.html
|--src/
| |--a.js
| |--b.js
| |--c.js
| |--index.js
|--package.json
|--webpack.config.js

index.js

1
2
3
4
5
6
7
//index.js
import a from './a.js';
import b from './b.js';
function fn() {
console.log('index-------');
}
fn();

a.js

1
2
3
4
5
6
7
//a.js
require('./c.js');
const $ = require('jquery')
function fn() {
console.log('a-------');
}
module.exports = fn();

b.js

1
2
3
4
5
6
7
//b.js
require('./c.js');
const $ = require('jquery')
function fn() {
console.log('b-------');
}
module.exports = fn();

c.js

1
2
3
4
5
//c.js
function fn() {
console.log('c-------');
}
module.exports = fn();

webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: {
index: './src/index.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html'
})
]
}

不配置任何splitChunks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> webpack --mode=development

Hash: 58f075d584eee63560d0
Version: webpack 4.44.2
Time: 229ms
Built at: 2020/10/20 下午3:49:40
Asset Size Chunks Chunk Names
index.html 247 bytes [emitted]
index.js 324 KiB index [emitted] index
Entrypoint index = index.js
[./src/a.js] 157 bytes {index} [built]
[./src/b.js] 129 bytes {index} [built]
[./src/c.js] 80 bytes {index} [built]
[./src/index.js] 151 bytes {index} [built]
+ 1 hidden module
Child HtmlWebpackCompiler:
1 asset
Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
[./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 474 bytes {HtmlWebpackPlugin_0} [built]

可以看到index.js模块大小为324k,里面包含a.js,b.js,c.js,jquery

初步配置splitChunks

1
2
3
4
5
6
7
8
9
10
11
12
//webpack.config.js

optimization: {
splitChunks: {
cacheGroups: {
default: {
name: 'common',
chunks: 'initial',
},
}
}
}

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> webpack --mode=development

Hash: 5d698c9c939438c2aa3b
Version: webpack 4.44.2
Time: 226ms
Built at: 2020/10/20 下午3:52:26
Asset Size Chunks Chunk Names
common.js 320 KiB common [emitted] common
index.html 280 bytes [emitted]
index.js 6.22 KiB index [emitted] index
Entrypoint index = common.js index.js
[./src/a.js] 157 bytes {common} [built]
[./src/b.js] 129 bytes {common} [built]
[./src/c.js] 80 bytes {common} [built]
[./src/index.js] 151 bytes {common} [built]
+ 1 hidden module
Child HtmlWebpackCompiler:
1 asset
Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
[./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 474 bytes {HtmlWebpackPlugin_0} [built]

可以看到多了common.js模块大小为320k,里面包含a.js,b.js,c.js,jquery

添加权重priority,并且抽离node_modules中的第三方包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
optimization: {
splitChunks: {
minSize: 30,
cacheGroups: {
default: {
name: 'common',
chunks: 'initial',
priority: -20
},
vendors: { //拆分第三方库(通过npm|yarn安装的库)
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial',
priority: -10
}
}
}
},

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> webpack --mode=development

Hash: 40141540a6afd27e4c34
Version: webpack 4.44.2
Time: 237ms
Built at: 2020/10/20 下午4:00:24
Asset Size Chunks Chunk Names
common.js 2.27 KiB common [emitted] common
index.html 313 bytes [emitted]
index.js 6.23 KiB index [emitted] index
vendor.js 318 KiB vendor [emitted] vendor
Entrypoint index = vendor.js common.js index.js
[./src/a.js] 157 bytes {common} [built]
[./src/b.js] 129 bytes {common} [built]
[./src/c.js] 80 bytes {common} [built]
[./src/index.js] 151 bytes {common} [built]
+ 1 hidden module
Child HtmlWebpackCompiler:
1 asset
Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
[./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 474 bytes {HtmlWebpackPlugin_0} [built]

可以看到多出vendor.js,大小318kb,而index.js大小减少到6.23kb,说明jQuery已经被抽离了,同时业务逻辑代码也抽离出

抽离指定文件

src/common新增utils.js,并在index.js中引用他

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
optimization: {
splitChunks: {
minSize: 30,
cacheGroups: {
default: {
name: 'common',
chunks: 'initial',
priority: -20
},
vendors: { //拆分第三方库(通过npm|yarn安装的库)
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial',
priority: -10
},
utils:{
test:/[\\/]src[\\/]common[\\/]/,
name: 'utils',
chunks: 'initial',
priority: -9
}
}
}
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
> webpack --mode=development

Hash: ec7341e5722a3d0efd5a
Version: webpack 4.44.2
Time: 238ms
Built at: 2020/10/20 下午4:09:31
Asset Size Chunks Chunk Names
common.js 2.31 KiB common [emitted] common
index.html 345 bytes [emitted]
index.js 6.24 KiB index [emitted] index
utils.js 387 bytes utils [emitted] utils
vendor.js 318 KiB vendor [emitted] vendor
Entrypoint index = utils.js vendor.js common.js index.js
[./src/a.js] 157 bytes {common} [built]
[./src/b.js] 129 bytes {common} [built]
[./src/c.js] 80 bytes {common} [built]
[./src/common/utils.js] 34 bytes {utils} [built]
[./src/index.js] 148 bytes {common} [built]
+ 1 hidden module
Child HtmlWebpackCompiler:
1 asset
Entrypoint HtmlWebpackPlugin_0 = __child-HtmlWebpackPlugin_0
[./node_modules/html-webpack-plugin/lib/loader.js!./public/index.html] 474 bytes {HtmlWebpackPlugin_0} [built]

可以看到utils.js已经被单独抽离成一个chunks,至此已经实现抽离第三方包,业务代码,工具模块,指定模块包

结语

splitChunks的抽离能节省许多重复代码的引入,加载。对项目大小与用户体验起到很大作用