这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战
背景
我们有一个微信小程序,在迭代的过程中,为了赶进度,最初里面大部分页面都是利用 web-view 组件嵌套h5站点来实现的;h5站点,就是利用的vue-cli官方那一套搭建的单页面应用;随着项目的推进,迭代的节奏开始慢下来,老生常谈的前端项目优化问题,终于被提上了日程(主要有人吐槽加载太慢了)
调研
由于h5站点是用微信小程序 web-view 组件渲染,所以曾经有一个方向是去看微信文档,去小程序社区看看,有没有解决方法,由此找到了下面几篇文章
分析过后,其实大部分的问题好像都和web-view组件关系不是很大,那还是回到h5站点项目的优化上吧
落地方案
直接上结论,优化的部分主要有以下几个点:
单页面应用转多页面(多入口)
项目经过几个迭代之后,很多h5站点的页面都被抛弃了,或被原生重写;那么此刻单页面应用,对于h5站点的加载速度,会有很大影响;因为那些用不上的页面js,引用的第三方库或者组件,可能会被打入 vendor.js 中,导致整理包体积过大。
-
1、去除 vue-router
-
2、生成各个入口文件的js文件 (这里是用 node 写的脚本,目的就是生成类似于 main.js 的入口文件)
-
2、配置 vue.config.js
// vue.config.js
const glob = require('glob');
const path = require('path');
// 获取所有的页面入口文件
// 本项目所有入口js文件都以 `Html.js` 结尾 且都在 views 文件夹下
const PAGE_PATH = path.resolve(__dirname, './src/views/**/*Html.js')
const pages = {};
// 利用 glob.sync 遍历文件,组成如下pages 结构
/**
* pages = {
index: 'src/views/indexHtml.js',
home: 'src/views/homeHtml.js',
cart: 'src/views/cartHtml.js',
}
*/
glob.sync(PAGE_PATH).forEach(filePath => {
var filename = filePath.replace(/(.*)src\/(.*)\/(.*)Html\.js/,'$3');
var fileAbsolutePath = filePath.replace(/(.*)src\/views\/(.*)\.js/,'$2');
pages[filename.toLowerCase()] = `src/views/${fileAbsolutePath}.js`
})
module.exports = {
// ...code
pages
// ...code
}
上面的 pages 项,这么配置之后,构建完成之后就会生成多个 [entry].html文件,但是模版都是用的 public/index.html(埋下了坑)
分析依赖 -> chunk-vendor.js 过大
项目中,我们主要用到了的第三方库有 vue、echarts、tim-js-sdk、js-cookies
上面这些第三方库,默认都被构建打包到chunk-vendor.js中,所以这个js显得格外的大,并且它被各个入口的.html文件引用
于是我们把这些包单独用 script 标签,在index.html里引入,并配置 configureWebpack.externals,从而减小 chunk-vendor.js 的体积
配置方法可以参考 外部扩展(Externals)
// vue.config.js
let webpackConfig = {
// ...code
// ...code
}
if(process.env.NODE_ENV === 'production') {
webpackConfig.configureWebpack.externals = {
'vue':"Vue",
'echarts':'echarts',
// ...
}
}
module.exports = webpackConfig
除此之外,我们还做了一件事,修改了 index.html
<!DOCTYPE html>
<html lang="cn">
<head>
</head>
<body>
<noscript>
<strong>We're sorry but vue-base doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
<% if(process.env.NODE_ENV==='production' ){ %>
<script src="<%= process.env.VUE_APP_publicPath %>/vue.runtime.min.js"></script>
<script src="<%= process.env.VUE_APP_publicPath %>/echarts.min.js"></script>
<% } %>
</body>
</html>
!!! 可以看出,只有在生产环境中,我们才会启用 externals 配置,原因是:公司网络不好,外部js引入老是超时 😢😢😢
异步引入组件
异步组件,可以看我这篇文章 Vue 异步组件和动态组件(2.x)
一些非首屏需要用到,且体积过大的组件,可以异步引入;只有在组件被用到的时候,才会去异步请求(ajax);
// xxx.vue
<script>
import LargeAndNotNeedComp from '../components/LargeAndNotNeedComp.vue'
// ...code
export default {
name:'CreatPatient',
components:{LargeAndNotNeedComp},
// ...code
}
</script>
// 改写为 👇👇👇👇👇
<script>
// ...code
export default {
name:'CreatPatient',
components:{
'LargeAndNotNeedComp': () => import('../components/LargeAndNotNeedComp.vue')
},
// ...code
}
</script>
其他优化
其他优化手段还有 路由懒加载,cdn,terser 删除注释、console ,由于项目建立初期就已经做了,就不介绍了
填坑
在第一步 [单页面到多页面] 和 第二步 [分析依赖] 的过程中,其实埋下了一个不小的坑;
由于 public/index.html 是所有入口文件的公共页面,在我们把许多第三方库用 configureWebpack.externals 配置项,从chunk-vendor.js中拆出,放入 public/index.html 异步引入的过程中,没有考虑到很多页面其实用不上这些库,但也引入了,造成了不小的问题;
最后调整配置,只把所有页面都需要的第三方包,才放入 configureWebpack.externals 中,如 Vue;一些特殊页面需要用到的第三方库,依然采用import的方式引入(二三级页面),并调整 vue.config.js
let pages = {};
// ...code
let webpackConfig = {
// ...code
chainWebpack: config => {
config.optimization.splitChunks({
cacheGroups: {
vendors: {
name: 'chunk-vendors',
minChunks: Object.keys(pages).length,
test: /node_modules/,
priority: -10,
chunks: 'initial'
},
common: {}
}
});
}
}
上方的关键配置在 minChunks: Object.keys(pages).length,表示只有所有页面都用到了的模块(node_modules里),才会被构建到chunk-vendor.js中
总结
优化的目的,其实就是要解决加载慢的问题;
那么如果项目的资源体积小了,整体加载自然就会快;
一个很大的js文件,拆分成多个小文件,并行加载,整体加载也会快;
去除用不上的文件,或者说用到了再加载,首次整体加载也会快;
还有,如果请求过一次,下次能利用缓存,那整体加载也会快
结果
某个页面的优化前后对比:
优化前:
js文件请求数量:12
js加载总大小:777kb
dom加载完成时间:711ms
优化后:
js文件请求数量:6
js加载总大小:237kb
dom加载完成时间:282ms