H5性能优化实践

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-预加载技术(未采用)
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.压缩图片(已采用)

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

  2. 雪碧图

  3. 图片懒加载

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

相关推荐
EveryPossible11 分钟前
终止异步操作
前端·javascript·vue.js
Stringzhua30 分钟前
setup函数相关【3】
前端·javascript·vue.js
neon120440 分钟前
解决Vue Canvas组件在高DPR屏幕上的绘制偏移和区域缩放问题
前端·javascript·vue.js·canva可画
Sammyyyyy1 小时前
Node.js 做 Web 后端优势为什么这么大?
开发语言·前端·javascript·后端·node.js·servbay
妮妮喔妮1 小时前
Webpack 有哪些特性?构建速度?如何优化?
前端·webpack·node.js
ST.J1 小时前
webpack笔记
前端·笔记·webpack
Baklib梅梅1 小时前
2025 年 8 个最佳网站内容管理系统(CMS)
前端·ruby on rails·前端框架·ruby
IT_陈寒2 小时前
🔥5个必学的JavaScript性能黑科技:让你的网页速度提升300%!
前端·人工智能·后端
Bling_Bling_12 小时前
面试常考:js中 Map和 Object 的区别
开发语言·前端·javascript
前端小巷子2 小时前
JS实现丝滑文字滚动
前端·javascript·面试