沐光

记录在前端之路的点点滴滴

webpack 文件分离思想

前言

先前做 vue 项目时,对于代码分割做了一个 DllPlugin 的 demo ,项目内的收益也挺不错(编译时间减少了 20s 左右),但是今天在看文档时,无意间又扫到了 SplitChunksPlugin。那么问题就来了,先前用的是 DllPlugin ,它和这个 SplitChunksPlugin 又有什么区别呢。这里简单记录下调研所获得的一些收获。

SplitChunksPlugin

自 webpack 4.0 上线之后,CommonsChunkPlugin 已被替换成 SplitChunksPlugin,旨在优化 chunk 的拆分。了解 SplitChunksPlugin 之前,我们需要知道为什么会弃用 CommonsChunkPlugin,CommonsChunkPlugin 的设计思路是什么,目前的 SplitChunksPlugin 在原来的思路上有什么改进。因此先看看 CommonsChunkPlugin 的设计思路

CommonsChunkPlugin 的设计思路

原本的 CommonsChunkPlugin 的思路为:

Create this chunk and move all modules matching minChunks into the new chunk

即:

满足 minChunks 的引用次数时,都会将对应的模块抽离如一个新的 chunk 文件中,这个文件为所有的业务文件的父级。

而这种设计思路带来了会造成模块打包冗余。总的来说会造成这么几个问题:

  • 产出的 chunk 在引入时,会包含重复的代码;
  • 无法优化异步 chunk;
  • 高优的 chunk 产出需要的 minchunks 配置比较复杂。

让我们具体来看下面两种情况。

产生多余模块

假如我们的文件是这么配置的:

1
2
3
4
5
minChunks: 2

entryA: vuex vue Acomponent
entryB: vue axios BComponent
entryC: vue vuex axios CComponent

那么产出的文件为:

1
2
vendor-chunk: vuex vue axios
chunkA~chunkC: only the Component

带来的问题:entryB 并没有使用 vuex,entryA 并没有使用 axios ,但是从产出的文件上来看,entryA 与 entryB 都引入了部分“脏”模块,这并不太好。

异步支持差

除此之外,CommonsChunkPlugin 对于异步的模块不是很友好。例如:

1
2
3
4
5
minChunks: 2

entryA: vuex vue Acomponent
asyncB: vue axios BComponent
entryC: vue vuex axios CComponent

产出的 chunk 为:

1
2
3
4
vendor-chunk:vue vuex
chunkA: only the components
asyncB: vue axios someComponents
chunkC: axios someComponents

带来的问题:异步引入 asyncB 之后会引入过多的 module。

SplitChunksPlugin 的设计思路

SplitChunksPlugin 优化了 webpack 的打包策略,使用自动重复算法,会自动计算出各页面公共的包引用以及部分页面公共的包引用,当然,对于那些部分共有但是阈值过小的文件其不会创建单独的输出文件,因为其大小不值得去新开一个请求。(缓存策略配置在 cacheGroup 中)

注:减少 maxInitial/AsyncRequest 会加大 module 的冗余,但是会进一步的减少请求。

SplitChunksPlugin 默认的分包策略基于以下 4 个条件:

  1. 新代码块可以被共享引用,或这些模块都是来自 node_modules;
  2. 新产出的 vendor-chunk 的大小得大于 30kb;
  3. 按需加载的代码块(vendor-chunk)并行请求的数量不多于 5 次;
  4. 初始加载的代码块,并行请求的数量不多于 3 次。

SplitChunksPlugin 配合使用 RuntimeChunk 对运行时的 hash 变动做优化(相当于 CommonsChunkPlugin 的两次使用)

DllPlugin

DLLPlugin 这个插件是在一个额外独立的 webpack 设置中创建一个只有 dll 的 bundle,也就是说,除了 webpack.config.js,项目中还会新建一个 webpack.dll.config.js 文件来配置 dll 的打包。webpack.dll.config.js 作用是把所有的第三方库依赖打包到一个 bundle 的 dll 文件里面,还会生成一个名为 manifest.json 文件。

该 manifest.json 的作用是用来让 DllReferencePlugin 映射到相关的依赖上去的。(可类比 CommonsChunkPlugin 的两次打包或者 RuntimeChunk 的运行包配置)

DllPlugin 的设计思路

SplitChunksPlugin 虽然也是将公共模块抽离,但是其每次打包的时候还是会去处理一些第三方依赖库,只是它能把第三方库文件和我们的代码分开掉,生成一个独立的 js 文件。但是它还是不能提高打包的速度

DLLPlugin 它则是提前将公共的包构建出来,使得在 build 时过滤掉这些构建过的包,使得在正是构建时的速度缩短。所以其相对来说打包速度会更快

总结

总的来说,两种打包的方式各有优劣。

如果开发的项目是锁定版本的,同时想要提升构建速度,这时候可以考虑使用 DllPlugin 的分包策略,提前将不变的一些依赖构建好,每次构建时仅构建业务代码即可。

如果项目会升级,同时想要减少后期对于打包步骤的操作(构建时间不考虑),那么优先使用 SplitChunksPlugin 来配置分包策略,毕竟 DllPlugin 得走两步。

对于两者一起使用的情况,目前查阅过两者一起使用的例子(貌似会打包重复,有冲突)不过网上的结论比较陈旧,笔者暂未做测试,之后空闲下来再做补充。

参考文档