H5性能优化实践

249 阅读4分钟

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 工具生成性能报告

原因

一、网络延迟导致加载慢

  1. 使用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.压缩图片(已采用)

  1. 使用工具 tinypng.com/ 压缩图片,建议使用webp格式的图片

  2. 雪碧图

  3. 图片懒加载

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秒左右