webpack官网中,有这么一句话,我不是很认同:
可以说,性能优化最重要的部分就是懂得如何分包了。因此,就来谈谈webpack中的分包。
首先我们需要了解一个概念。
什么是chunks
一个常考的面试题是 module、chunk、bundle是什么。对于初学者,这个是很迷惑的。我们首先要知道webpack一个简单的理解:
Webpack: 构建你的assets。左侧的资源通过webpack后,都能输出web浏览器能识别的资源! 归因于node构建了一个世界,让这个世界所有非js资源,都在js世界下的规则下处理:
css: 本质是style下的一个字符串,webpack处理css文本后,如果要直接作为style标签插入,再使用style-loader,如果要拿出来,则通过MiniCssExtractPlugin插件进行处理。 less: 本质最终要转化成css,因此通过less-loader,转化为css,再重复上面的操作。 typescript: 浏览器是没法直接识别的,通过ts-loader。来转化为浏览器能识别的js。
以上这些,都是为了做一件事:
以上图webpack为划分点,我们就可以区分module和bundle。即:左边是资源就是moudle,右边的资源就是bundle。
一个ts文件、图片、less、pug等都是一个module,而打包后的产物,总的称呼就是bundle。
那么chunk是什么呢?
对于打包产物bundle, 有些情况下,我们觉得太大了。 为了优化性能,比如快速打开首屏,利用缓存等,我们需要对bundle进行以下拆分,对于拆分出来的东西,我们叫它chunk。
我们试着打包一下!
默认配置
现在我们创建src/index.js 和 src/a.js
index.js | a.js | import lodash from "lodash";,import { a } from "./a";,console.log(lodash, a); | export const a = "i am aaaaaa";,console.log(a);, |
---|
目录结构:
webpack.config.js 配置和打包结果为:
webpack.config.js | {, mode: "production",, entry: {, main: "./src/index.js",, },, output: {, path: path.resolve(__dirname, "dist"),, filename: "[name].js", , clean: true,, },,} | | |
---|
默认的,webpack没有进行分包,全部都打在一起了。
一些配置字段
Webpack 的分包主要在optimization.splitChunks属性,现在我们来尝试使用不同的配置字段来看看效果。
optimization.splitChunks.chunks
Chunks 有三个提供的值,分别是 async、initial、all
async
此值是默认的chunks值,也就是说,我们的第一次打包实际上就是实行了async,该值的意思是:对于动态加载的模块,默认配置会将该模块单独打包。使用以下语法进行动态加载(还有其他写法):
import('lodash')
修改index.js,然后运行build命令,为了文件更直观,我们将optimization.chunkIds的值设置为named
index.js | import { a } from "./a";,import('lodash').then(lodash => {, const res = lodash.default.add(3,4), console.log(a, res);,}) |
---|
可以看到lodash被分出一个单独的包了。
以下是webpack对于默认配置的说法:
进行实验后,发现并不准确,比如两个入口引入lodash,lodash并未被抽出来
index.js | other.js | import lodash from "lodash";,import { a } from "./a";,console.log(lodash, a); | import lodash from "lodash";,,console.log("lodash", lodash); |
---|
配置与打包
配置 | 打包 | {, entry: {, main: "./src/index.js",, other: "./src/other.js",, },, optimization: {, chunkIds: "named",, },,} | | | 默认配置只会抽出动态加载的模块,通常情况下,不是立即需要的包,可以考虑动态加载,比如导出excel的包,echarts,monaco-editor等。 | #### initial |
---|
当chunk为initial或者为alll时,webpack打包遵循以下配置(取名default):
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'initial',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
};
尽管你的配置可能是这样:
{, entry: {, main: "./src/index.js",, other: "./src/other.js",, },, optimization: {, chunkIds: "named",, splitChunks: {, chunks: "initial",, },, },,} |
---|
只有当chunks 不为async时,webpack打包的默认配置才会是default配置
all
当chunks值为all时,基本跟initial值相同,我们来实验一下它的不同点;
我们将在other.js对lodash动态引用:
import('lodash').then(lodash => {
const res = lodash.default.add(3,4)
console.log(res);
})
然后分别使用chunks为all 和initial的值,看看效果:
all | initial |
---|
可以看到,对于两个入口文件引用lodash, 如果一个是正常引入,一个是动态引入,initial会打包成两份,而all的话,只会有一份,因此,通常情况下,all的优于initial的。
optimization.splitChunks的其他默认配置
{
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
}
minSize:此配置是指,将要被分包的chunks,如果压缩前体积不足20k,将不会被拆包。 minChunks:某个chunks被多次引用,如果这个引用次数小于某个值,将不会被拆包。 ... 以上条件满足一个将会被分包。 enforceSizeThreshold:如果某个chunks的大小超过了50k,以上限制将不会生效。
optimization.splitChunks. cacheGroups
cacheGroups有两个默认缓存策略,也就是chunks为all和initail时的默认配置:
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
}
defaultVendors 会将源代码中所有引入node_modules的文件打包成为一个大的chunks。 default 则是对于多入口引入的相同模块超过两次后,进行拆包操作,需要注意的是,我们通常操作的单页面应用,默认只有一个入口文件,如果有如下代码:
// index.js
import lodash from "lodash";
import { a } from "./a";
import { b } from "./b";
console.log(lodash, a, b);
// a.js
export const a = "i am aaaaaa";
console.log(a);
import "./c";
// b.js
export const b = "i am bbbbbbbbb";
console.log(b);
import "./c";
// c.js
console.log("ccccccccc");
a.js 和 b.js 共同引用了c.js。此时a、b、c都从属于index.js入口,虽然c.js被引用了两次,但c.js并不会分成单独的包,如果要将c.js单独打包,考虑动态加载。
通常,我们只需要默认的配置即可满足大部分需求,有的时候我们可能想单独抽出react相关代码,那么这需要以下配置。
react: {
name: "ReactAbout",
test: /react/,
priority: 1,
},
打包效果
// other.js
import('lodash').then(lodash => {
const res = lodash.default.add(3,4)
console.log(res);
})
import('./style/a.css')
import('./style/b.css')
import('./style/c.css')
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
}
css
css也是性能优化的一部分,有一种方式是通过style- loader将css以style标签的形式插入到文档,这种方式正常引用无法进行分包。但可以通过动态引入的方式分包。
另外有一个MiniCssExtractPlugin插件进行css的分包。此插件默认为每个入口单独抽出css,也可以进行cacheGroups的配置,满足条件时,会将多个入口的css打包在一起。
css: {
name: "css",
test: /\.css$/,
minChunks: 1,
enforce: true,
}
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!