Taro + webpack5 + vue3项目,h5页面性能加载问题
背景:pda设备较差,cpu处理能力不足,h5页面首屏加载缓慢,vconsole调试工具查看接口耗时很长(几十秒),但用抓包工具、服务端接口请求日志查看发现接口处理时间很快(几百毫秒),中间的差值到底耗时在哪里?vconsole的time并不是指服务器接口响应的时间,还包括资源排队的时间,排队时间长估计就是主要原因了
思路:用谷歌浏览器cpu降速20倍模拟场景,用屏幕录制找出耗时长的长任务(是否加载了除了首屏之外的其他资源、某个模块包是否太大阻塞了主线程)
性能指标
指标名称 | 英文缩写 | 定义 | 测量方法 |
首屏绘制时间 | FCP | 页面首次内容绘制的时间 | 使用Performance API获取first-contentful-paint时间 |
可交互时间 | TTI | 页面达到完全可交互状态的时间 | 使用 Lighthouse 工具生成性能报告 |
最大内容绘制时间 | LCP | 页面上最大内容元素(如图片、文本块)绘制的时间 | 使用PerformanceObserver监听largest-contentful-paint事件 |
累积布局偏移 | CLS | 页面在加载过程中发生的布局偏移总量 | 使用 Lighthouse 工具生成性能报告 |
总阻塞时间 | TBT | 页面从开始加载到完全可交互期间,主线程被阻塞的总时间 | 使用 Lighthouse 工具生成性能报告 |
原因
一、网络延迟导致加载慢
- 使用preload-预加载技术(未采用)
js
<!-- 关键图标资源使用 preload -->
<link rel="preload" href="/static/js/iconfont.js?v=3.1.2" as="script" crossorigin>
<!-- 非关键图标资源使用 prefetch -->
<link rel="prefetch" href="/static/js/mobile-color.js?v=3.1.2" as="script">
2.使用cdn (未采用)
二、资源太大导致加载慢
1.分包 chunk(已采用)
js
// config.index.js文件
webpackChain(chain, webpack) {
chain.optimization.splitChunks({
chunks: 'all',
minSize: 100000, // 提高到 100KB
minChunks: 2,
maxAsyncRequests: 8,
maxInitialRequests: 4, // 限制首屏请求数
automaticNameDelimiter: '~',
cacheGroups: {
// 核心库 - 首屏必需
coreVendors: {
test: /[\\/]node_modules[\\/](vue|pinia|@vue|@tarojs)[\\/]/,
name: 'chunk-core-vendors',
priority: 90, // 最高优先级
chunks: 'initial', // 只包含初始 chunk
enforce: true, // 忽略 minSize 限制
reuseExistingChunk: true,
},
// 大型库 - 第一大类异步加载
largeVendors: {
test: /[\\/]node_modules[\\/](vue-pdf-embed|vue3-echarts)[\\/]/,
name: 'chunk-large-vendors',
priority: 80,
chunks: 'all',
enforce: true,
reuseExistingChunk: true,
},
// 大型库 - 第二大类异步加载
secondLargeVendors: {
test: /[\\/]node_modules[\\/](nutui-taro|@nutui|echarts|lodash|lodash-es|vconsole|vuedraggable|)[\\/]/,
name: 'chunk-secondLarge-vendors',
priority: 70,
chunks: 'all',
minChunks: 1,
minSize: 0, // 不限制最小大小
enforce: true, // 忽略 minSize 限制
reuseExistingChunk: true,
},
// 大型库 - cnhis-design-taro单独打包
cnhisDesign: {
test: /[\\/]node_modules[\\/](cnhis-design-taro)[\\/]/,
name: 'chunk-cnhisDesign-vendors',
priority: 60,
chunks: 'all',
enforce: true,
reuseExistingChunk: true,
},
// 大型库 - cnhis-ai-agent-taro单独打包
cnhisAiAgent: {
test: /[\\/]node_modules[\\/]@cnhis-frontend[\\/]ai-agent-taro[\\/]/,
name: 'chunk-aiAgent-vendors',
priority: 50,
chunks: 'all',
enforce: true,
reuseExistingChunk: true,
},
// 其他 node_modules 中的依赖 添加排除规则
vendors: {
test: /[\\/]node_modules[\\/](?!(vue|@vue|pinia|@tarojs|nutui-taro|@nutui|vue-pdf-embed|cnhis-design-taro|ai-agent-taro|vue3-echarts|echarts|lodash|vconsole|vuedraggable))[^\\/]+[\\/]/,
name: 'chunk-vendors',
priority: 10,
chunks: 'all',
minSize: 200000, // 200KB 以上才打包
reuseExistingChunk: true,
minChunks: 1
},
// 公共组件
common: {
test: path.resolve(__dirname, "src/components"),
name: 'chunk-common',
priority: 5,
minChunks: 3,
reuseExistingChunk: true
},
// 工具函数
utils: {
name: "chunk-utils",
test: path.resolve(__dirname, "src/utils"),
priority: 8,
reuseExistingChunk: true,
minChunks: 2
},
// 图标资源
icons: {
test: /[\\/]src[\\/]assets[\\/]js[\\/]/,
name: 'icons',
priority: 5,
minChunks: 1,
reuseExistingChunk: true,
minSize: 10000
},
}
});
}
2.页面组件、第三方库按需引入(已采用)
按需加载的实现基于 ES6 的动态导入和打包工具(如 Webpack、Rollup)的代码分割功能。当打包工具遇到 import() 表达式时,会自动将被导入的模块分割成单独的代码块(chunk),只有在运行时执行到 import() 语句时才会加载相应的代码块。
js
import { defineAsyncComponent } from 'vue';
const HeaderPatientBox = defineAsyncComponent(() => import('@/pages-clinurse/components/header-patient-box/index.vue'))
使用 Webpack 的 import() 动态导入语法,按需加载第三方库,而不是一次性全部加载,选择性价比较好的第三方库,
js
import { cloneDeep, range } from 'lodash-es'
3.Tree Shaking(摇树优化),移除未使用的代码,减小文件体积。打包工具已经支持,一般无需额外操作
4.nginx缓存(已采用)
- 强缓存:Cache-Control: max-age=31536000(1年)
- 协商缓存:Etag/Last-Modified(适合频繁更新资源)
- 离线缓存:Service Worker + Cache API实现离线可用
5.压缩图片(已采用)
-
使用工具 tinypng.com/ 压缩图片,建议使用webp格式的图片
-
雪碧图
-
图片懒加载
6.SSR服务端渲染(未采用)--- 终极方案
监控工具
使用插件 webpack-bundle-analyzer
,生成report.html
报告,根据报告分析包的合理性
js
1.安装插件
yarn add webpack-bundle-analyzer --dev
2.添加配置
// config.index.js文件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; webpackChain(chain, webpack) {
// 添加 Bundle Analyzer
chain.plugin('bundle-analyzer')
.use(BundleAnalyzerPlugin, [{
analyzerMode: 'static', // 生成静态 HTML 报告
openAnalyzer: false, // 不自动打开浏览器
generateStatsFile: false, // 生成 stats.json
defaultSizes: 'gzip', // 显示 gzip 后大小
reportFilename: path.resolve(process.cwd(), 'report.html'),
statsFilename: path.resolve(process.cwd(), 'stats.json'),
}]);
}
3.在package.json的scripts里添加命令
"report": "set NODE_ENV=production && taro build --type h5"
4.最后执行yarn report 即可生成report.html文件
谷歌浏览器降速20倍检测,最终首屏加载快了5-6秒左右