问题发生背景
vue项目首屏加载缓慢,f12一看有的js文件很大,有1.3M旁边,还有个chunk文件也有1.2M,但是目前不知道里面打包了什么东西,导致入口文件加载缓慢,导致发接口的时机延长,所以看见首页的东西就很晚了。
所以原因是:首屏加载的js缓慢,导致页面空白时间太久。
优化思路
先把打包报告打出来看一下,打包了哪些东西?
1. 安装个webpack打包工具插件,查看打包报告
打包报告:
可以看到有两个js文件很大,9300那个大部分是图片打包到了js里面,在代码里面看一下这个文件:
可以看到webpack打包,把某些文件图片转换成了base64图片格式,图片又很多,导致整个的js文件很大。
按照这个思路试想一下:能不能不让图片转换成base64格式?
查了一下文档发现,webpack会默认把某些较小的图片文件打包成base64,为了加快访问图片,不用请求资源,原本是一件好事,没想到我们这个项目图片太多了,导致用这个配置之后,反而加慢了整体的进程。
2. 设置图片限制,不打包成base64格式。
const { defineConfig } = require("@vue/cli-service");
const BundleAnalyzer = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
module.exports = defineConfig({
publicPath: process.env.VUE_APP_CURENVs,
outputDir: "h5_integral_mall", // 打包名称
lintOnSave: process.env.NODE_ENV !== "production",
transpileDependencies: true,
productionSourceMap: false,
css: {
loaderOptions: {
scss: {
additionalData: `@import "~@/assets/css/app.scss";`,
},
},
},
// 该配置表示,图片大小最大为1KB,小于1KB的才会转换为base64
chainWebpack: (config) => {
config.module
.rule("images")
.test(/\.(png|svg|jpg|jpeg|gif)$/)
.set("parser", {
dataUrlCondition: {
maxSize: 1,
},
});
}
});
上面优化配置项要注意的是:不同的webpack版本对应不同的写法,需要对照webpack官网尝试。当时试了好久不成功,是因为webpack版本不同,写法也没生效。
打包后试一下:
可以看到文件已经变小了。但是打包完后整体的包的img文件夹大了,是正常的,因为图片都被放出来了。
3. chunk文件过大,拆包处理。
chainWebpack: (config) => {
config.module
.rule("images")
.test(/\.(png|svg|jpg|jpeg|gif)$/)
.set("parser", {
dataUrlCondition: {
maxSize: 1,
},
});
// 开发环境不拆包
// 分模块和优先级进行拆分
if (process.env.NODE_ENV !== "development") {
config.optimization.splitChunks({
chunks: "all",
automaticNameDelimiter: "-",
cacheGroups: {
vant: {
name: "chunk-vant",
priority: 32,
test: /[\\/]node_modules[\\/]_?vant(.*)/,
},
mathjs: {
name: "chunk-mathjs",
priority: 35,
test: /[\\/]node_modules[\\/]_?mathjs(.*)/,
},
vue: {
name: "chunk-vuejs",
priority: 30,
test: /[\\/]node_modules[\\/](vue|vue-router|vuex)[\\/]/,
},
// src/components组件从chunk里拆分出来
components: {
name: "chunk-components",
test: resolve("src/components"),
minChunks: 2,
priority: 5,
reuseExistingChunk: true,
},
},
});
config.optimization.runtimeChunk("single");
}
}
分模块和优先级进行拆分。
打包后如下:
可以看出从之前的一个chunk文件拆分成了好几个chunk文件。整个项目的文件大小也从6.39M减小到4.66M。
4. moment插件处理
从分析报告可以看出:moment文件加有一个local文件,查了一下文档,这个文件主要是用于各个地区的时间处理,由于项目中没使用到,主要是在中国时区,所以可以考虑去除该模块。
configureWebpack: (config) => {
if (process.env.NODE_ENV !== "development") {
config.plugins.push(
new BundleAnalyzer({
// 设置混淆选项
openAnalyzer: true,
analyzerPort: 1000,
})
);
// 去除moment文件夹中的local文件。
config.plugins.push(
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
})
/* 压缩loader */
);
}
},
打包后如下:
5. 开启CSS代码压缩
const { defineConfig } = require("@vue/cli-service");
const TerserPlugin = require("terser-webpack-plugin");
const BundleAnalyzer = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const webpack = require("webpack");
/* 开启css压缩 */
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = defineConfig({
publicPath: process.env.VUE_APP_CURENVs,
outputDir: "h5_integral_mall", // 打包名称
lintOnSave: process.env.NODE_ENV !== "production",
transpileDependencies: true,
productionSourceMap: false,
css: {
loaderOptions: {
scss: {
additionalData: `@import "~@/assets/css/app.scss";`,
},
},
},
configureWebpack: (config) => {
if (process.env.NODE_ENV !== "development") {
config.plugins.push(
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
})
);
config.optimization.minimizer = [
/* 开启css压缩 */
new CssMinimizerPlugin(),
];
}
},
chainWebpack: (config) => {
config.module
.rule("images")
.test(/\.(png|svg|jpg|jpeg|gif)$/)
.set("parser", {
dataUrlCondition: {
maxSize: 1,
},
});
},
});
6. CDN加载外部资源
在vue.config.js中,配置哪些外部资源需要cdn加载:
/* 根据环境动态配置cdn资源 */
let externals = {};
let CDN = { css: [], js: [] };
/* 非开发环境 */
if (process.env.NODE_ENV !== "development") {
externals = {
/**
* externals对象属性分析:
* '包名':'在项目中引入的名字'**/
lodash: "lodash",
mathjs: "mathjs",
vconsole: "vconsole",
};
CDN = {
css: [],
js: [
"https://cdn.bootcdn.net/ajax/libs/mathjs/12.3.0/math.min.js",
"https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js",
"https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.1/vconsole.min.js",
],
};
}
紧接上面的chainWebpack配置:
chainWebpack: (config) => {
config.module
.rule("images")
.test(/\.(png|svg|jpg|jpeg|gif)$/)
.set("parser", {
dataUrlCondition: {
maxSize: 1,
},
});
},
config.plugin('html').tap(args => {
// 参数对象添加属性叫cdn,值就是上面CDN对象
args[0].cdn = CDN// 配置CDN给插件
return args
})
接下来在html页面中,循环配置中的cdn资源:
<!-- 导入js -->
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
最后打包看一下:
可以看到打包后,打包文件里已经没有我们在config.js里面配置的外部资源了。有效的减少了包的文件大小。从之前的6.38M减少到3.69M。
在打包后的index.html可以看到,加载了我们配置的js包。
这个cdn加载有个注意的是::要找个靠谱的cdn网站,我之前配置了之后,cdn崩了一天,所以这种方案后面就没有使用。
7. 关于图片还有其他的思路,就是挺麻烦
这个思路适合在项目初期建设阶段,就是将项目中的图片文件放在public文件夹下,这样webpack就不会将图片打包。注意的就是,在代码中引用图片资源的写法就要注意,都要写成相对路径。
大部分图片有三种格式:
require方式引用图片css,backgrundImage设置背景图片- 还有就是在模板标签中使用图片
首先在vue.config.js里面配置一下环境变量,可以在css中写env中配置的环境变量。
module.exports = defineConfig({
publicPath: process.env.VUE_APP_CURENVs,
outputDir: "h5_xiaoyiyunxuan", // 打包名称
lintOnSave: process.env.NODE_ENV !== "production",
transpileDependencies: true,
productionSourceMap: false,
css: {
// css增加环境变量,在css中就可以使用$env-host来替换项目的域名和端口
loaderOptions: {
scss: {
additionalData: `$env-host:"${process.env.VUE_APP_HOST}";$env-curenvs:"${process.env.VUE_APP_CURENVs}";@import "~@/assets/css/app.scss";`,
},
},
require方式引用图片格式代码改写:
css,backgrundImage设置背景图片
- 在模板标签中使用图片
总结
通过打包报告会有思路,去哪方面去优化,有问题解决问题,就是自己对webpack配置项还是不熟悉,很多配置还是需要去查,去搜。慢慢学习吧~