共计 3404 个字符,预计需要花费 9 分钟才能阅读完成。
该文章内容大致翻译自 webpack 4: Code Splitting, chunk graph and the splitChunks optimization
原有的问题
webpack 4.0 对代码模块的关系图进行了一些巨大的优化, 同时添加了一个新的 optimization 用于模块的分离(可以看做是对 CommonsChunkPlugin 的一次优化)。
先让我们看看旧版关系图的一些缺陷。
在之前的版本中,我们将各个模块打包进编译后的文件之中,同时这些文件之间又是通过父子关系来进行关联, 最后将我们整个项目中的所有模块串联起来。
当其中一个文件含有父级引用,那我们可以推断出,在该文件完成加载时,已经成功加载了父级文件。那么我们可以据此进行一些优化。比如当一个文件中的模块已经在父级文件中正常运行,那么我们可以将该模块从文件中移除,因为它必然已经被成功加载。
在入口点或异步拆分点处引用这些文件。这些文件将会并行加载。
这种类型的关系图使得分离 splitting 变得非常困难。比如在你使用 CommonsChunkPlugin 插件时。会有一个或多个文件内模块被移动到新的文件中来。这一整个新文件需要被添加到关系图中来。但我们应该如何设置它的层级呢?作为旧有文件的父级?还是子级?CommonsChunkPlugin 中将其设置为父级,但从技术层面来说,这是错误的,并且导致了一些负面的优化结果(提前加载的这个文件中的部分模块不是必需的)。
现在新的关系图中,引入了一个新概念:ChunkGroup。包含文件列表的文件分组。
在入口点或异步拆分点处我们会引用这个文件分组,该分组内的文件全都是并行加载。而一个文件可以被多个文件分组引用(但不会多次加载)。
现在不再使用父子级的关系来描述文件之间的联系,取而代之的是文件分组 ChunkGroup。
那么此时,splitting 文件就能够被表述出来。被创建的新文件可以被添加到所有包含原始文件的文件分组中。同时也不会因此产生负面优化效果。
CommonsChunkPlugin 和 SplitChunksPlugin 的区别
这个问题被修复之后,我们可以更多的使用文件拆分了。我们可以将任何文件拆分出来并且不需要提高其加载优先级。
CommonsChunkPlugin 存在以下这些问题:
需要下载当前还不需要使用的代码文件。
异步加载使用文件效率低下。
很难使用。(猜测这里指的是配置)
实现方式很难理解。
所以新的插件诞生了:SplitChunksPlugin。
它会使用模块引用计数和模块类别区分 (比如:node_modules) 来自动分离出需要被拆分的文件内引用模块。
There is a paradigm shift here. The CommonsChunkPlugin was like:“Create this chunk and move all modules matching minChunks into the new chunk”. The SplitChunksPlugin is like:“Here are the heuristics, make sure you fullfil them”. (imperative vs declarative)这里没有理解全部的内容。
SplitChunksPlugin 同时提供了更多的特性:
不会加载非必须文件(除非进行了强制合并)
异步文件处理更有效率。
默认异步处理文件。
它将引用模块分散到多个库文件中。
更容易使用。
不依赖文件引用关系图。
更加的自动化。
例子
下面是一些使用 SplitChunksPlugin 的例子。这些用例仅仅展现了它在默认配置下的行为。你也可以使用额外配置项来进行个性化定制。
提示:
可以通过 optimization.splitChunks 进行配置。这里的例子是关于文件的,默认情况下仅适用于异步加载的文件块。但也可以添加 optimization.splitChunks.chunks: “all” 来配置适用于所有类型的文件。
我们假定每个额外导入的库文件都大于 30kb,因为优化仅在该体积之后开始进行。(可以通过配置 minSize 属性进行修改,默认 30000)
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 将会自动创建 2 个库文件:
vendors~chunk-a~chunk-b: react, react-dom
vendors~chunk-c~chunk-d: angular
chunk-a chunk-b chunk-c chunk-d: 仅含有 components
Vendors overlapping
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 依然会创建 2 个库文件:
vendors~chunk-a~chunk-b~chunk-c: react, react-dom
vendors~chunk-b~chunk-c: lodash
chunk-a chunk-b chunk-c: 仅含有 components
Shared modules
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
假设所有的 shared components 体积都大于 30kb,webpack 将会创建一个库文件和一个通用组件文件:
vendors~chunk-a~chunk-b~chunk-c: vue
commons~chunk-a~chunk-b~chunk-c: some shared components
chunk-a chunk-b chunk-c: 仅含有 components
当这些 shared components 体积小于 30kb 是,webpack 会故意将该模块复制到 chunk-a chunk-b chunk- c 三个文件中。我们认为进行分离所减小的加载体积的整体效果并不如一次额外的加载请求的消耗。
Multiple shared modules
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 将会创建 2 个库文件及 2 个通用组件文件
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 chunk-b chunk-c chunk-d: Only the components
提示:因为生成的导入文件名称包含所有的原始文件名称,所以我们推荐在生产环境中使用的长效缓存文件不要包含 [name] 在文件名中,或者设置 optimization.splitChunks.name: false 来关闭文件名生成逻辑。这样即使在后续开发中对该文件添加了新的引用,也不会修改文件名,该缓存逻辑依然生效。