记一次SPA项目打包优化的过程
前言
在完成spa
项目打包上线后,最让人困扰的一个spa
的缺点就是首屏加载事件过长,在之前的解决方案中我们通常是添加一个loading
页面来减少用户等待所带来的焦虑感,但是这样的解决方案治标不治本,我们还需要从根本出发。
原因分析
首先我们分析spa
项目的解析原理,从浏览器内输入地址后开始下载静态资源,静态资源下载完成后在执行reader
渲染元素节点。通过浏览器 F12
中查看文件下载的速度,结果发现 chunk-vendors.js
文件竟然达到了将近 10M
,阻塞我们的页面渲染进程。
解决方案
了解 chunk-vendros.js
的文件是什么在此时至关重要,这个时候我们可以通过 webpack-bundle-analyzer
插件进行分析。
// vue.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
configureWebpack: config => {
config.plugins.push(new BundleAnalyzerPlugin());
},
}
通过分析 chunk-vendros.js
文件,我们发现使我们引入第三方插件导致 chunk-vendros.js
文件过大,文件过大就导致了静态资源下载过大从而网页白屏时间更长。
为了减少白屏时间我们就必须要删除插件的引入,删除插件的引入那项目就不能运行,但是我们还可以使用 CDN
进行引入插件,这样就减少了将第三方插件打包到 chunk-vendors.js
文件中。
移除第三方插件
externals
可以不引入插件,它是 key-value
的形式,其中 key
与 package.json
配置文件中的 dependencies
保持一致,而 value
则是相对应的全局属性了,全局属性就需要我们去 第三方插件 的 源码里面寻找了。
首先我们已 element-ui
为例,首先我们需要让 webpack
不打包 element-ui
插件,我们需要用到 webpack
的 externals
module.exports = {
configureWebpack: config => {
config.externals = {
'element-ui': 'ELEMENT'
};
}
}
element-ui
对应的全局属性是 ELEMENT
;
!function(e,t){
// ....
e.ELEMENT=t(e.Vue)...
}
一般是在第三方插件是这样的格式,我们去寻找 e 后面复制的属性一般就是,其次我们可以在控制台中打印,一般全局变量和插件名字相同。
当配置完这一步之后再打打包,项目就会报错,因为我们的插件没有打包上去,但是我们需要用到它。
引入在线CDN
可以在 html
页面直接引入在线的 CDN
,也可以通过插件的方式打包引入。
// vue.config.js
const cdn = {
externals: {
'element-ui': 'ELEMENT',
},
js: [
"https://unpkg.com/[email protected]/lib/index.js",
],
css: [
'https://unpkg.com/[email protected]/lib/theme-chalk/index.css'
]
};
<!-- 循环引入 css cdn -->
<head>
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>
</head>
<!-- 循环引入 js cdn -->
<body>
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
不仅是第三方插件,我们的 vue,vuex,vue-router 都是可以通过 cdn 引入的。
再次打包,我们发现此时的文件已经减少了一部分,页面似乎有更快了一点,但是效果也不是恒明显。
开始gzip压缩
为了达到更好的体验效果,时候我们还可以开启 gzip
压缩代码。
const CompressionWebpackPlugin = require('compression-webpack-plugin');
configureWebpack: config => {
config.plugins.push(new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8
}));
config.plugins.push(new BundleAnalyzerPlugin());
config.externals = cdn.externals;
},
开启了gzip
压缩代码后,打包文件中活多出了gz
的文件,这个就是我们的压缩文件,已经是非常小的。此外需要请我们的运维为我们的项目开启 gzip
后浏览器才能访问压缩的文件,再次访问,页面访问的速度已经大幅度提升了。
细节优化
此外我们还可以从代码层面进行优化,比如说路由懒加载,使用 transform-remove-console
插件清除 console
输出。
// bable.config.js
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
],
plugins: ['transform-remove-console']
}
问题总结
spa
单页面程序为我们带来便利的同时也为我们带来了一些不可避免的缺点,而页面渲染白屏时间过长是一个最大的缺点了,而解决这方面的问题就是减少插件的使用,尽量使用CDN
来加载;通过gzip
压缩压缩文件,其次就是一下代码规范问题了。