webpack4实践笔记8 关于SplitChunksPlugin

发布 : 2019-03-11 分类 : webpack 浏览 :

本系列代码放在 github 上: webpack4系列实践代码
如果本系列实践教程对你有帮助,欢迎给个 github star 哦!

1.SplitChunksPlugin 介绍

从 webpack4 开始,用 SplitChunksPlugin 插件替换了 CommonsChunkPlugin 插件。
相对于 CommonsChunkPlugin ,SplitChunksPlugin 的使用更加方便和清晰。

SplitChunksPlugin 是 webpack4 内置的开箱即用的代码块分离插件,webpack4 会根据你配置好 optimization.splitChunks 规则,进行代码分割,打包成不同的代码块。这样的好处在于避免代码重复引用,减少代码体积,按需加载,从而使浏览器资源加载速度快,并且减少服务器的压力和带宽。

SplitChunksPlugin 文档:https://www.webpackjs.com/plugins/split-chunks-plugin/

2.splitChunks参数配置

  • chunks (默认是async) :initialasyncall
  • minSize (默认是30000):新代码块最小体积(压缩之前),单位:字节(b)
  • minChunks(默认是1):代码块至少被引用的次数
  • maxInitialRequests (默认是3):入口处的最大并行请求数
  • maxAsyncRequests (默认是5):按需加载时最大的并行请求数
  • test (默认所有模块): 匹配需要处理的模块, 可以传的值类型: RegExp、String和Function
  • automaticNameDelimiter (默认是’~’): 打包分隔符
  • priority:缓存组打包的先后优先级

属性优先级 minSize > maxSize > maxAsyncRequests > maxInitialRequest

这里重要介绍一下 chunks 的三个属性 initialasyncall

  • async 只对对动态(异步)导入的模块进行分离
  • initial 对所有模块进行分离,如果一个模块既被异步引用,也被同部引用,那么会生成两个包
  • all 对所有模块进行分离,如果一个模块既被异步引用,也被同部引用,那么只会生成一个共享包

具体可以参考这篇文章: Webpack 4 Mysterious SplitChunks Plugin

3.安装相关依赖

通过 html-webpack-plugin 自动生成 index.html

1
2
npm install -D html-webpack-plugin
npm install -D webpack // html-webpack-plugin 依赖于 webpack

3.目录结构

1
2
3
4
5
6
7
8
9
10
// `--` 代表目录, `-` 代表文件
--demo08
--src
-app1.js
-app2.js
-module1.js
-module2.js
-module3.js
-index.html
-webpack.config.js

src/module1.js

1
export const module1 = 'module1';

src/module2.js

1
export const module2 = 'module2';

src/module3.js

1
export const module3 = 'module3';

src/app1.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
25
import { module1 } from './module1';
import { module2 } from './module2';

// module1 module2 属于同步加载
console.log('app1: ', module1);
console.log('app1: ', module2);

// 通过require.ensure对module3.js进行加载调用(此写法在ts模式支持)
setTimeout(() => {
require.ensure(
[],
function () {
const module3 = require("./module3");
console.log('app1: ', module3.module3);
},
"module3"
);
}, 3000);

// // 异步加载的另一种写法(此写法在ts模式下不支持)
// setTimeout(() => {
// import(/* webpackChunkName: 'module3'*/ "./module3").then(function (module3) {
// console.log(module3.module3);
// });
// }, 3000);

src/app2.js

1
2
3
4
5
6
7
8
import { module1 } from './module1';
import { module2 } from './module2';
import { module3 } from './module3';

// module1 module2 module3 都属于同步加载(注意对比app1.js)
console.log('app2: ', module1);
console.log('app2: ', module2);
console.log('app2: ', module3);

4.编写 webpack 配置文件

webpack.config.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
25
26
27
28
const path = require("path");
module.exports = {
mode: 'production' || 'development',
entry: {
app1: "./src/app1.js",
app2: "./src/app2.js"
},
output: {
publicPath: __dirname + "/dist/", // 打包后资源文件的引用会基于此路径
path: path.resolve(__dirname, "dist"), // 打包后的输出目录
filename: "[name].bundle.js",
chunkFilename: "[name].chunk.js"
},
optimization: {
// runtimeChunk: "single", // 使用single模式,可以避免每一个包都包含webpack的运行文件
splitChunks: {
cacheGroups: {
modules: {
chunks: "async" || "initial" || "all", // 三选一
minChunks: 1, // 代码块至少被引用的次数
maxInitialRequests: 3, // 设置最大的请求数
minSize: 0, // 设置每个chunk最小的大小,只有大于这个值,才会被打包进一个chunk
automaticNameDelimiter: '~'
}
}
}
}
};

5.执行打包命令

(默认你已经安装了全局 webpack 以及 webpack-cli )

1
webpack

打包成功后,结果输出在 demo08 的dist目录下

在浏览器运行 dist/index.html,打开控制台可以观察异步加载模块的效果。

6.调试参数并验证输出结果

这里可以通过修改以上参数(比如 chunks , minChunks , minSize)的值来查看不同的输出结果。

如上 webpack.config.js 配置文件,设置 minSize0 ,以及 minChunks1 以确保满足一个新块的条件。

当设置 chunksasync

webpack只对异步加载的模块进行分离,所以输出结果为:

1
2
3
app1.bundle.js
app2.bundle.js
modules~module3.chunk.js

(注意以上例子中有两个入口文件 app1.js app2.js)

app1.js相关:

1
2
app1.bundle.js ( app1 主模块,并且包含 module1.js 和 module2.js)
modules~module3.chunk.js

app2.js相关:

1
app2.bundle.js ( app2 主模块,并且包含 module1.js、module2.js 和 module3.js)

当设置 chunksinitial

webpack对所有模块进行分离,如果一个模块既被异步引用,也被同部引用,那么会生成两个包,所以输出结果为:

1
2
3
4
5
6
app1.bundle.js
app2.bundle.js
module3.chunk.js
modules~app1.chunk.js
modules~app1~app2.chunk.js
modules~app2.chunk.js

app1.js相关:

1
2
3
4
app1.bundle.js (app1主模块)
module3.chunk.js (包含 module3.js 异步加载模块)
modules~app1.chunk.js (包含app1独有的模块: 这里暂时没有)
modules~app1~app2.chunk.js (包含 module1.js 和 module2.js)

app2.js相关:

1
2
3
app2.bundle.js (app2主模块)
modules~app2.chunk.js (包含app2独有的模块: 这里是module3.js)
modules~app1~app2.chunk.js (包含 module1.js 和 module2.js)

当设置 chunksall

webpack对所有模块进行分离,如果一个模块既被异步引用,也被同部引用,那么只会生成一个共享包,所以输出结果为:

1
2
3
4
5
app1.bundle.js
app2.bundle.js
modules~app1.chunk.js
modules~app1~app2.chunk.js
modules~app2~module3.chunk.js

app1.js相关:

1
2
3
4
app1.bundle.js ( app1 主模块)
modules~app1.chunk.js (包含 app1 独有的模块: 这里暂时没有)
modules~app1~app2.chunk.js (包含 module1.js 和 module2.js)
modules~app2~module3.chunk.js (包含 module3.js 模块,虽然名字没有 app1 ,但是可以通过测试知道,app1 是依赖此模块的,比如把此模块删除,然后在 index.html 中引入上面三个文件,查看控制台就能知道 app1 依赖此模块)

app2.js相关:

1
2
3
app2.bundle.js ( app2 主模块)
modules~app1~app2.chunk.js (包含 module1.js 和 module2.js)
modules~app2~module3.chunk.js (包含 module3.js )

7.bundle vs chunk

不知你注意到没有,以上打包结果中,app1 的主模块(app1.bundle.js)和app2的主模块(app2.bundle.js)的文件名包含的是 bundle,而其他模块包含的是 chunk。

bundle 表明该代码块包含有 webpack 运行时 的代码。而 chunk 则没有。
那你可能会问,这样 app1.bundle.js 和 app2.bundle.js 就包含了同样的 webpack 运行时代码,可以把这份 webpack 运行时的代码单独分离出来作为一个单独的块吗?
答案是可以的。

在 webpack.config.js 配置文件中添加以下属性即可:

1
runtimeChunk: "single"

这样在以上 chunks 为 all 的示例中,输出的结果为:

1
2
3
4
5
6
app1.chunk.js
app2.chunk.js
modules~app1.chunk.js
modules~app1~app2.chunk.js
modules~app2~module3.chunk.js
runtime.bundle.js ( webpack 运行时模块)

8.源码地址

demo 代码地址: https://github.com/SimpleCodeCX/simple-webpack-demos/tree/master/demo08-SplitChunksPlugin
仓库代码地址(及目录): https://github.com/SimpleCodeCX/simple-webpack-demos

参考文档:

Webpack 4 Mysterious SplitChunks

SplitChunksPlugin官方文档

本文作者 : Simple
原文链接 : https://simplecodecx.github.io/blog/20190311/ca3604dd.html
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
留下足迹