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-预加载技术(未采用)
<!-- 关键图标资源使用 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(已采用)
// config.index.js文件
webpackChain(chain, webpack) {
chain.optimization.splitChunks({
chunks: 'all',
minSize: 100000, // 提高到 100KB
minChunks: 2,
maxAsyncRequests: 8,
maxInitialRequests: 4, // 限制首屏请求数
automaticNameDelimiter: '~',
cacheGroups: {
// 大型库 - 第一大类异步加载
largeVendors: {
test: /[\\/]node_modules[\\/](vue-pdf-embed|vue3-echarts)[\\/]/,
name: 'chunk-large-vendors',
priority: 80,
chunks: 'async',
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,
},
// 将 AI 相关的大型库单独分包
aiLibraries: {
name: 'chunk-ai-libraries',
test: /[\\/]node_modules[\\/](mermaid|elkjs|dagre|d3|cytoscape)/,
chunks: 'async', // 设置为异步加载
priority: 25,
reuseExistingChunk: true,
},
// 公共组件
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,
},
},
})
}
2.页面组件、第三方库按需引入(已采用)
按需加载的实现基于 ES6 的动态导入和打包工具(如 Webpack、Rollup)的代码分割功能。当打包工具遇到 import() 表达式时,会自动将被导入的模块分割成单独的代码块(chunk),只有在运行时执行到 import() 语句时才会加载相应的代码块。
import { defineAsyncComponent } from 'vue';
const HeaderPatientBox = defineAsyncComponent(() => import('@/pages-clinurse/components/header-patient-box/index.vue'))
使用 Webpack 的 import() 动态导入语法,按需加载第三方库,而不是一次性全部加载,选择性价比较好的第三方库,
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报告,根据报告分析包的合理性
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秒左右