添加分析工具
想要实时查看打包变化,可以通过一个打包分析利器。webpack-bundle-analyzer
安装
npm i webpack-bundle-analyzer --save-dev
package.json里面添加命令
"analyz": "NODE_ENV=production npm_config_report=true npm run build"
webpack配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [new BundleAnalyzerPlugin()],
执行
npm npm run analyz
工具配置好之后
首先看下webpack的默认打包配置
module.exports = {
optimization: {
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
}
}
}
}
};
解析下参数
- chunk: 含义是拆分模块的范围,它有三个值async、initial和all。
async表示只从异步加载得模块(动态加载import())里面进行拆分 initial表示只从入口模块进行拆分 all表示以上两者都包括
- minSize:最小拆分组件大小
- minChunks:最小引用次数
- maxAsyncRequests:限制异步模块内部的并行最大请求数的
- maxInitialRequests:允许入口并行加载的最大请求数
- automaticNameDelimiter:文件名的连接符
- name: split 的 chunks name
- cacheGroups:缓存组
默认为vendors和default。可以设置权重值priority。
vendor一般放置node_modules里面的文件, default放置公共组件
增加模块标识
我们需要加添加几个文件进行测试
但是现在个问题
假如我先建立一个btest文件,再建立一个atest文件,进行打包后,发现btest的文件的hash也会发生变化。
这是因为,webpack在建立依赖的时候的文件是按照ascall码进行排序的,然后新插入的文件就会发生变化。
解决办法,
给模块的名字加上标识。这样在打包的时候就会按照标识进行。
配置如下
optimization: {
namedChunks:true, //增加模块标识
splitChunks: {
// 配置
}
},
plugins: [
new webpack.HashedModuleIdsPlugin(), //模块增加标识,开发环境建议用 NamedModulePlugin
],
再进行打包,发现问题解决,再回到我们的打包方案分析。
打包分析
现在打包有几种方案
1. node—modules和公共组件(比如loading)引用超过两次以上都会打包到base文件。
webpack配置如下
optimization: {
splitChunks: {
cacheGroups: {
base:{
chunks: 'initial', //initial表示提取入口文件的公共部分
minChunks: 2, //表示提取公共部分最少的文件数
minSize: 0, //表示提取公共部分最小的大小
name: 'base' //提取出来的文件命名
}
}
}
},
打包分析结果如下
现在我们在atest里面引入loading组件<template>
<div>
atest
</div>
</template>
<script>
import Loading from '../components/loading/loading' //引入Loading组件
export default {
}
</script>
再次打包
和我们想的一样,只有atest发生变化 然后在btest里面再引入loading再次打包 发现 atest btest base 都发生了变化。
因为loading被引入超过了两次,loading先从atest抽离,atset发生变化。base加入loading组件发生变化。 但是这就是隐患之一,但是我们可以把minChunks设置为1,这样的话,所有被引用的组件都会被base.js里面去的。
但是如果base文件太大的话,缓存的代价太大了,所以我们换一个策略
2. 分离式打包,node_modules会单独打包,每个引入的组件会打包到一个文件中。 webpack配置如下
optimization: {
namedChunks:true,
splitChunks: {
chunks: 'all',
minSize: 0,
minChunks: 1,
maxAsyncRequests: 10,
maxInitialRequests: 10,
// automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
minChunks: 1,
test: /[\\/]node_modules[\\/]/,
priority: -10 // 权重
},
default: {
minChunks: 1,
priority: -20, // 权重
reuseExistingChunk: true
}
}
}
},
设置两个缓存组:其中vendors是放node_modules打包后的文件
老样子,添加atest文件和btest文件
打包分析
现在我们在atest里面引入loading组件 ,然后在btest文件页引入loading组件 打包分析 其中的atest~btest就是分离出来的loading组件,但是这个配置是存在一个问题的?可以看到我在default里面将minChunks的值改成1,代表是当组件被引用超过一次就会打包出来,我们看下,只有atest引入loading引入的时候的情况 只有atest引入
<div>
atest
</div>
</template>
<script>
import myCom from '../components/testCom/testCom'
export default {
}
</script>
打包分析
可以看到引入的testCom组件并没有被单独打包出来。但是我们设置了minChunks:1,按理说testCom组件应该被分出来,但是结果并没有,现在猜想是可能只满足了被引用一次,但是没有满足其他条件,或者是因为key设置为了default的原因。这个等之后再做研究,我们现在先替换掉default,换回我们的base
vendors: {
minChunks: 1,
test: /[\\/]node_modules[\\/]/,
priority: -10, // 权重
minSize: 0,
},
base: {
priority: -20, // 权重
chunks: 'initial', //initial表示提取入口文件的公共部分
minChunks: 1, //表示提取公共部分最少的文件数
minSize: 0, //表示提取公共部分最小的大小
name: '
}
来看下打包结果
node_modules引用打包到了vendors,公共组件打包到base里面,一切都如此美好,这个也能满足开发需求。下面我们来研究一个新的东西
3. 全部打散式打包,node_modules会打成包,每个组件自己单独打包。
现在我们已经实现了分离打包,公共组件,比如loading都打进了base文件,但是如果我们想把loading单独打包出来,形成loading.js,或者我们再引入一个toast组件,也要打包成toast.js文件,这个怎么进行操作?也有办法!
先上代码
vendors: {
name:'vendors',
minChunks: 1,
test: /[\\/]node_modules[\\/]/,
priority: -10, // 权重
minSize: 0,
},
default:{
test:/[\\/]components[\\/]|[\\/]common[\\/]/,
priority:-20, // 权重
name(module){
// console.log('模块分析打印')
// console.log(module.identifier())
const moduleFileName = module
.identifier()
.split('/')
.pop()
.replace('.js','')
return `${moduleFileName}`
}
},
其中的components是我存放公共组件的地方,common是存放公共js方法的地方。我只需要筛选他们下面的引入
打包结果如下
现在再引入一个公共方法
<template>
<div>
atest
</div>
</template>
<script>
import myCom from '../components/testCom/testCom'
import '../common/widget/common'
export default {
}
</script>
在看打包结果
可以看出testCom单独打包,而且commmon也单独打包了,ok!解决 !这样比较上面方式的好处是,上面那种方法,当你只改变一个组件的内容时候,会引起整个base的变化。这个弊端会随着base文件的越来越大而越来越️明显
代码
最后放一下三种方案的代码
1. node—modules和公共组件(比如loading)引用超过两次以上都会打包到base文件。
optimization: {
namedChunks: true,
splitChunks: {
chunks: 'all',
minSize: 0,
minChunks: 1,
maxAsyncRequests: 100,
maxInitialRequests: 100,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
base: {
minChunks: 1, //表示提取公共部分最少的文件数
minSize: 0, //表示提取公共部分最小的大小
name: 'base' //提取出来的文件命名
}
}
}
2. 分离式打包,node_modules会单独打包,每个引入的组件也会单独打包。
optimization: {
namedChunks: true,
splitChunks: {
chunks: 'all',
minSize: 0,
minChunks: 1,
maxAsyncRequests: 100,
maxInitialRequests: 100,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
minChunks: 1,
test: /[\\/]node_modules[\\/]/,
priority: -10, // 权重
minSize: 0,
},
base: {
priority: -20, // 权重
chunks: 'initial', //initial表示提取入口文件的公共部分
minChunks: 1, //表示提取公共部分最少的文件数
minSize: 0, //表示提取公共部分最小的大小
name: 'base' //提取出来的文件命名
}
}
}
3. 全部打散式打包,node_modules会打成包,每个组件自己单独打包。
optimization: {
namedChunks: true,
splitChunks: {
chunks: 'all',
minSize: 0,
minChunks: 1,
maxAsyncRequests: 100,
maxInitialRequests: 100,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
vendors: {
name:'vendors',
minChunks: 1,
test: /[\\/]node_modules[\\/]/,
priority: -10, // 权重
minSize: 0,
},
default:{
test:/[\\/]components[\\/]|[\\/]common[\\/]/,
priority:-20, // 权重
name(module){
// console.log('模块分析打印')
// console.log(module.identifier())
const moduleFileName = module
.identifier()
.split('/')
.pop()
.replace('.js','')
return `${moduleFileName}`
}
}
}
}