学习webpack时官网推荐文档使用CommonsChunkPlugin,但The CommonsChunkPlugin 已经从
webpack v4 legato 中移除.借鉴其他写的文章加上原文进行总结.
参考: 没有了CommonsChunkPlugin,咱拿什么来分包 [1]:
https://blog.csdn.net/songluy...
起初,chunks(代码块)和导入他们中的模块通过webpack内部的父子关系图连接.CommonsChunkPlugin被用来避免他们之间的依赖重复,但是没有更多的优化.
从4.0版本开始CommonsChunkPlugin被移除且被optimization.splitChunks和optimization.runtimeChunk配置项代替.下面展示它们将如何工作.
Defaults
SplitChunksPlugin 拆箱即用 对于大多数的用户很好用.
默认情况下它将只会影响按需加载的代码块,因为改变初始化的代码块将会影响html中运行项目需要包含的script标签.
webpack将会基于以下条件自动分割代码块:
- 新的代码块被共享或者来自node_modules文件夹
- 新的代码块大于30kb(在min+giz之前)
- 按需加载代码块的请求数量应该<=5
- 页面初始化时加载代码块的请求数量应该<=3
为了满足以上最后两个条件,倾向于更大的代码块.
让我们看一些简单的例子.
Defaults: 例子1
// index.js
// dynamically import a.js
import("./a");
// a.js
import "react";
// ...
结果:一个包含react的单独代码块将会被创建.因为index.js在import '/a.js'时和a.js中都会记载这个代码块.
原因:
- 条件1:代码块包含来自node_modules的模块
- 条件2:react>30kb
- 条件3:代码块的引用数量是2
- 条件4:不影响初始页面加载
这样做的理由是? react 不会像你的应用代码一样经常改变.通过将它放到一个单独的代码块中可以与你自己的代码分离开来,从而被独立缓存(假设你正在用chunkhash, records, Cache-Control等长期缓存策略).
Defaults: 例子 2
// entry.js
// dynamically import a.js and b.js
import("./a");
import("./b");
// a.js
import "./helpers"; // helpers is 40kb in size
// ...
// b.js
import "./helpers";
import "./more-helpers"; // more-helpers is also 40kb in size
// ...
结果:一个包含'./helpers'和所有关于它的依赖的代码块将会被创建.当调用import时,这个代码块一起被加载进原始代码块.
原因:
- 条件1:这个代码块被两个import调用共享
- 条件2:helpers>30kb
- 条件3:import同时请求为数量2
- 条件4:不影响页面初始化加载
将helpers的内容放进各个代码块将会导致helpers的代码被下载两次.通过使用分离的代码块将会只下载一次.我们将会多发起一起请求helpers的请求,如果代码块过小则不值得多发起一次请求,折中的建议是代码块超过30kb再进行分离.
配置(Configuration)
为了让开发者们对这个功能有更多的控制,webpack提供了一些配置项来更好的满足你的需求.
在人为改变分割配置时,请确定自定义配置带来了实实在在的好处.
默认配置即是我们推荐的web最佳实践,但是你项目的最佳策略根据项目类型可能会有所不同
配置缓存组(Configuring cache groups)
默认将所有来源于node_modules的模块分配到叫做'venders'的缓存组,所有引用超过两次的模块分配到'default'缓存组.
一个模块可以被分配到多个缓存组.这个优化可以通过选择更高优先级的缓存组或者形成代码块更大的缓存组来完成.
条件(Conditions)
当满足下列所有条件时,来自同一个代码块和缓存组的模块将会形成一个新的代码块.
下面条件的4个配置项:
- minSize (default: 30000) 代码块的最小尺寸
- minChunks (default: 1) 在分割之前模块的被引用次数
- maxInitialRequests (default 3) 一个入口的最大并行请求数量
- maxAsyncRequests (default 5) 按需加载最大并行请求数量
命名(Naming)
可以通过'name'配置项来控制切割之后代码块的命名.
给多个分割之后的代码块分配相同的名称,所有的vendor 模块被放进一个共享的代码块中,不过这会导致多余的代码被下载所以并不推荐
神奇的true值将会自动根据切割之前的代码块和缓存组键值(key)自动分配命名,否则就需要传入一个String或者function.
命名与入口名称相同时,入口将会被移除.
optimization.splitChunks.automaticNameDelimiter
默认webpack将会使用入口名和代码块的名称生成命名,比如 'vendors~main.js'
如果你的工程和"~"冲突,可以通过设置automaticNameDelimiter:'-'解决.
之后最终的命名将会是'vendors-main.js'
模块选择(Select modules)
test配置项控制这个缓存组所选择的的模块.忽略它选择所有模块.这个配置可以是正则,字符串或者是函数.
可以匹配绝对资源路径或者是代码块名.当一个代码块名称被匹配,所有在这个代码块中的模块都会被选则.
代码块选择(Select chunks)
可以用chunks配置项来指定代码块会被切割.
可用值:"initial","async","all".配置之后只会选择对应于初始化代码块,按需加载代码块,或者是所有代码块中的一个.
当模块完全匹配时,reuseExistingChunk 配置项允许重用已经存在的代码块而不是创建一个新的代码块.
每一个缓存组都可以使用这个配置项来控制.
optimization.splitChunks.chunks: all
就像之前提到的这个插件会影响动态导入的模块.设置chunks配置项为all将会影响初始化的代码块(即使不是动态导入的代码块).这种代码块将会被入口和按需加载共享.
下面是建议的配置:
这个配置可以配合HtmlWebpackPlugin插件使用,这将为你注入所有生成的vendor代码块.
optimization.splitChunks
下面的配置对象代表了splitChunksPlugin的默认行为.
splitChunks: {
chunks: "async",
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
缓存组默认继承splitChunks的配置项,但是test,priority和reuseExistingChunk只能在缓存组中被配置.
缓存组是一个是缓存组名字作为键值的对象.除了上述代码块中列举的配置项还有:chunks, minSize, minChunks, maxAsyncRequests, maxInitialRequests, name.
你可以设置optimization.splitChunks.cacheGroups.default = false来禁用默认缓存组,
vendors也可以同样禁用.
为了能够让自定义缓存组有更高的优先级(默认0),默认缓存组的priority属性为负值.
下面是一些例子和他们的影响:
Split Chunks: Example 1
创建名为"commons"的代码块,它包含了入口之间共享的所有代码.
splitChunks: {
cacheGroups: {
commons: {
name: "commons",
chunks: "initial",
minChunks: 2
}
}
}
这个配置会增大你的初始化包,当一个模块不是立即被需要建议采用动态导入.
Split Chunks: Example 2
创建一个vendor代码块,它包含了整个应用中所有来自node_modules的代码.
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all"
}
}
}
这会导致一个包含所有外部包的大代码块.建议只包括你的核心框架和功能,并且动态加载剩余的依赖.
注意:这可能会导致下载额外的代码。
optimization.runtimeChunk
设置optimization.runtimeChunk=true ,将每一个入口添加一个只包含runtime的额外代码块.
然而设置值为single,只会为所有生成的代码块创建一个共享的runtime文件.
runtime:连接模块化应用程序的所有代码.
runtime包含:在模块交互时,连接模块所需的加载和解析逻辑。包括浏览器中的已加载模块的连接,以及懒加载模块的执行逻辑.