原文地址: webpack 4: Code Splitting, chunk graph and the splitChunks optimization
原文作者: Tobias Koppers
译者: arzh
推荐理由: 从webpack3
升级到webpack4
所做的重要更改
webpack4
对chunk
图进行了一些重大改进,并为chunk
拆分添加了一个新的优化(这是对CommonsChunkPlugin
的一种改进)
让我们来看看旧版关系图的一些缺点
在旧图中,chunks
通过父子关系和chunks
包含模块来连接其他的chunks
当一个chunk
有多个父节点的话,我们可以认为当这个chunk
被加载的时候,至少有一个父节点已经被加载了.这个信息可以被用来优化,例如,当一个chunk
的模块,在所有父节点都可用时,它可以从chunk
中被移除,因为它一定已经被成功加载了。
在入口点或异步拆分点处引用chunk
列表时,这些chunks
会并行加载。
这种类型的关系图使得分离chunks
变得十分困难,例如使用CommonsChunkPlugin
会有这样的问题,当你删除一个或多个chunks
模块,并将它们放在一个新的chunk
中,这个新的chunk
需要被重新连接到关系图中,但是如何连接呢?作为旧的chunk的父级,还是子级?CommonsChunkPlugin
将其添加为父级,但这在技术上是错误的,同时也会对其他优化产生负面影响(父节点信息是不准确的)
新的chunk
图中引入了一个新对象: ChunkGroup
,一个ChunkGroup
包含多个chunk
在入口点或异步拆分点处引用单个ChunkGroup
,这意味着所有被ChunkGroup
包含的chunk
都是并行加载的。一个chunk
可以被引用在多个ChunkGroup
中(但不会被加载多次)
现在就不再使用父子关系来关联chunk
,而是通过ChunkGroup
来进行关联
现在chunks
的分割可以被理解了,新增加的chunks
会被添加到所有包含原始chunk
的ChunkGroups
中,这并不会对父层级关系产生负面影响。
现在这个问题被修复了,我们就可以开始更多的使用chunk
分离了,我们可以任意拆分chunk
而不用担心chunk
图会被破坏
CommonsChunkPlugin
有许多的问题:
- 会下载一些我们所不需要的代码
- 在异步
chunks
下是低效率的 - 会比较难使用
- 实践起来比较难以理解
所以新的插件诞生了:SplitChunksPlugin
它能通过heuristics
自动识别应该被分块的模块,使用模块重复计数和模块类别(如node_modules
),来分割chunks
这里有一种两者的比喻。CommonsChunkPlugin
就像:"创建一个chunk
并将匹配minChunks
的所有模块移动到新块中",SplitChunksPlugin
就像:"这是heuristics
,确保你满足他们"(Here are the heuristics, make sure you fullfil them)(命令式与声明式的区别)
SplitChunksPlugin
也有一些很棒的属性:
- 它永远不会下载不需要的模块(只要你不通过名称强制执行
chunk
合并) - 在异步
chunks
下也是高效的 - 默认为异步
chunks
模式分割 - 更加容易使用
- 不依赖于
chunk
图 - 大多是自动化配置的
以下是SplitChunksPlugin
为你列举的几个例子。这些示例仅显示默认行为,附加配置有更多个性化的选择
注意:你可以通过optimization.splitChunks
进行配置。这些示例说明了一些关于chunk
的内容,默认情况下它只适用于异步chunk
,但是使用optimization.splitChunks.chunks:"all"
来配置,适用于所有类型的文件(同步、异步chunk
)
注意:我们假设此处使用的每个外部库都大于30kb
,因为代码优化仅在超出此大小之后才能生效。
Vendors
chunk-a: react
, react-dom
, some components
chunk-b: react
, react-dom
, some other components
chunk-c: angular
, some components
chunk-d: angular
, some other components
webpack将会自动创建两个vendors chunks
,结果如下:
vendors~chunk-a~chunk-b: react
, react-dom
vendors~chunk-c~chunk-d: angular
chunk-a to chunk-d: Only the components
Vendors重叠
chunk-a: react
, react-dom
, some components
chunk-b: react
, react-dom
, lodash
, some other components
chunk-c: react
, react-dom
, lodash
, some components
webpack也会创建两个vendors chunks
,结果如下:
vendors~chunk-a~chunk-b~chunk-c: react
, react-dom
vendors~chunk-b~chunk-c:lodash
chunk-a to chunk-c: Only the components
共享模块
chunk-a: vue
, some components
, some shared components
chunk-b: vue
, some other components
, some shared components
chunk-c: vue
, some more components
, some shared components
假设共享组件的大小大于30kb
,webpack
将创建一个vendors chunk
和一个commons chunk
,结果如下:
vendors~chunk-a~chunk-b~chunk-c: vue
commons~chunk-a~chunk-b~chunk-c: some shared components
chunk-a to chunk-c: Only the components
当这些shared components
体积小于30kb
时,webpack
会特意将该模块复制到chunk-a chunk-b chunk-c
三个文件中。他们认为进行分离所减小的加载体积的整体效果并不如一次额外的加载请求的消耗。
多个共享模块
chunk-a: react
, react-dom
, some components
, some shared react components
chunk-b: react
,react-dom
, angular
, some other components
chunk-c: react
,react-dom
, angular
, some components
, some shared react components
, some shared angular components
chunk-d: angular
, some other components
, some shared angular components
webpack将创建两个vendors chunk
和两个commons chunk
,结果如下:
vendors~chunk-a~chunk-b~chunk-c: react
, react-dom
vendors~chunk-b~chunk-c~chunk-d: angular
commons~chunk-a~chunk-c: some shared react components
commons~chunk-c~chunk-d: some shared angular components
chunk-a to chunk-d: Only the components
注意:由于chunk
名称包括所有原始chunk
名称,因此建议使用长期缓存的生产版本不包括文件名中的[name]
,或通过optimization.splitChunks.name:false
关闭名称生成.这样在后续开发中添加了新的引用,也不会变更文件名。
对本文有任何优化建议,可扫描下述二维码一起讨论,同时也希望大家多多关注,会不定期发送一些原创文章