Vue 3 与 Performance API 高级性能监控

概述

Performance API 是浏览器提供的一组用于监控 Web 应用性能的 API,它允许开发者精确测量应用的各种性能指标,包括页面加载时间、资源加载时间、脚本执行时间、组件渲染时间等。与 Vue 3 结合使用,可以帮助开发者深入了解应用的性能瓶颈,优化用户体验。本集将深入探讨 Performance API 的高级特性和在 Vue 3 中的最佳实践,帮助你构建高性能的 Vue 3 应用。

核心知识点

1. Performance API 基础

Performance API 提供了多种性能测量方式,包括导航计时、资源计时、用户计时、长任务监控等。

// 获取性能对象
const performance = window.performance

// 获取导航计时数据
const navigationTiming = performance.getEntriesByType('navigation')[0]

// 获取资源计时数据
const resourceTimings = performance.getEntriesByType('resource')

// 获取用户计时数据
const userTimings = performance.getEntriesByType('measure')

// 获取长任务数据
const longTasks = performance.getEntriesByType('longtask')

2. 性能标记和测量

使用 performance.mark()performance.measure() 可以创建自定义性能标记和测量:

// 创建性能标记
performance.mark('start-task')

// 执行一些操作
heavyComputation()

// 创建结束标记
performance.mark('end-task')

// 创建测量(计算两个标记之间的时间差)
performance.measure('task-duration', 'start-task', 'end-task')

// 获取测量结果
const measure = performance.getEntriesByName('task-duration')[0]
console.log(`任务执行时间:${measure.duration}ms`)

// 清除标记和测量
performance.clearMarks()
performance.clearMeasures()

3. Vue 组件性能监控

使用组合式 API 监控 Vue 组件的渲染性能:

<!-- src/components/HeavyComponent.vue -->
<template>
  <div class="heavy-component">
    <h1>Heavy Component</h1>
    <div v-for="item in items" :key="item.id">
      {{ item.name }}
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'

const items = ref([])

// 组件挂载前标记
performance.mark('heavy-component-mount-start')

onMounted(() => {
  // 模拟大量数据渲染
  loadData()
  
  // 组件挂载后标记
  performance.mark('heavy-component-mount-end')
  performance.measure('heavy-component-mount', 'heavy-component-mount-start', 'heavy-component-mount-end')
})

onBeforeUnmount(() => {
  // 清除标记和测量
  performance.clearMarks('heavy-component-mount-start')
  performance.clearMarks('heavy-component-mount-end')
  performance.clearMeasures('heavy-component-mount')
})

async function loadData() {
  // 标记数据加载开始
  performance.mark('load-data-start')
  
  // 模拟异步数据加载
  await new Promise(resolve => setTimeout(resolve, 1000))
  
  // 生成大量数据
  items.value = Array.from({ length: 1000 }, (_, i) => ({
    id: i,
    name: `Item ${i}`
  }))
  
  // 标记数据加载结束
  performance.mark('load-data-end')
  performance.measure('load-data', 'load-data-start', 'load-data-end')
}
</script>

4. 路由切换性能监控

使用 Vue Router 导航守卫监控路由切换性能:

// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    { path: '/', name: 'home', component: HomeView },
    { path: '/about', name: 'about', component: () => import('../views/AboutView.vue') },
    { path: '/heavy', name: 'heavy', component: () => import('../views/HeavyView.vue') }
  ]
})

// 路由切换前
router.beforeEach((to, from, next) => {
  // 标记路由切换开始
  performance.mark(`route-${to.name}-start`)
  next()
})

// 路由切换后
router.afterEach((to, from) => {
  // 标记路由切换结束
  performance.mark(`route-${to.name}-end`)
  // 计算路由切换时间
  performance.measure(
    `route-${to.name}-transition`,
    `route-${to.name}-start`,
    `route-${to.name}-end`
  )
  
  // 获取并记录路由切换时间
  const measure = performance.getEntriesByName(`route-${to.name}-transition`)[0]
  console.log(`${to.name} 路由切换时间:${measure.duration}ms`)
})

export default router

5. 异步操作性能监控

监控异步操作(如 API 请求)的性能:

// src/utils/api.ts
import axios from 'axios'

const api = axios.create({
  baseURL: '/api'
})

// 请求拦截器
api.interceptors.request.use(config => {
  // 标记请求开始
  const requestId = `${config.method}-${config.url}`
  performance.mark(`${requestId}-start`)
  config.headers['X-Request-Id'] = requestId
  return config
})

// 响应拦截器
api.interceptors.response.use(response => {
  // 标记请求结束
  const requestId = response.config.headers['X-Request-Id']
  performance.mark(`${requestId}-end`)
  // 计算请求时间
  performance.measure(
    `${requestId}-duration`,
    `${requestId}-start`,
    `${requestId}-end`
  )
  
  // 获取并记录请求时间
  const measure = performance.getEntriesByName(`${requestId}-duration`)[0]
  console.log(`${response.config.url} 请求时间:${measure.duration}ms`)
  
  return response
})

export default api

6. 长任务监控

监控长任务(执行时间超过 50ms 的任务):

// src/utils/longTaskMonitor.ts
// 创建长任务监控器
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.warn(`长任务检测到:${entry.duration}ms`)
    console.warn('任务详情:', entry)
    
    // 可以将长任务信息发送到监控服务器
    // reportLongTask(entry)
  }
})

// 启动监控
observer.observe({ entryTypes: ['longtask'] })

export default observer

7. 内存监控

使用 performance.memory 监控内存使用情况:

// src/utils/memoryMonitor.ts
function monitorMemory() {
  if (performance.memory) {
    const memory = performance.memory
    console.log(`内存使用情况:
` +
      `  总量:${Math.round(memory.totalJSHeapSize / 1024 / 1024)}MB
` +
      `  已使用:${Math.round(memory.usedJSHeapSize / 1024 / 1024)}MB
` +
      `  限制:${Math.round(memory.jsHeapSizeLimit / 1024 / 1024)}MB`
    )
  }
}

// 定期监控内存使用
const memoryMonitorInterval = setInterval(monitorMemory, 5000)

export function stopMemoryMonitor() {
  clearInterval(memoryMonitorInterval)
}

8. 帧率监控

监控页面的帧率(FPS):

// src/utils/fpsMonitor.ts
class FPSMonitor {
  private frameCount = 0
  private lastTime = performance.now()
  private fps = 0
  private interval: number | null = null
  
  start(interval = 1000) {
    this.interval = window.setInterval(() => {
      const currentTime = performance.now()
      const elapsed = currentTime - this.lastTime
      
      // 计算帧率
      this.fps = Math.round((this.frameCount * 1000) / elapsed)
      
      // 发送帧率数据
      this.onFPSUpdate(this.fps)
      
      // 重置计数
      this.frameCount = 0
      this.lastTime = currentTime
    }, interval) as unknown as number
    
    // 监听动画帧
    this.tick()
  }
  
  private tick() {
    this.frameCount++
    requestAnimationFrame(() => this.tick())
  }
  
  stop() {
    if (this.interval) {
      clearInterval(this.interval)
      this.interval = null
    }
  }
  
  private onFPSUpdate(fps: number) {
    console.log(`当前帧率:${fps} FPS`)
    
    // 可以将帧率数据发送到监控服务器
    // reportFPS(fps)
  }
}

const fpsMonitor = new FPSMonitor()
export default fpsMonitor

9. 性能数据收集和分析

收集和分析性能数据:

// src/utils/performanceCollector.ts
function collectPerformanceData() {
  const data = {
    navigation: performance.getEntriesByType('navigation')[0],
    resources: performance.getEntriesByType('resource'),
    measures: performance.getEntriesByType('measure'),
    marks: performance.getEntriesByType('mark'),
    longTasks: performance.getEntriesByType('longtask'),
    memory: performance.memory || null
  }
  
  return data
}

function analyzePerformanceData(data: ReturnType<typeof collectPerformanceData>) {
  const analysis = {
    // 页面加载时间
    loadTime: data.navigation ? data.navigation.loadEventEnd - data.navigation.navigationStart : 0,
    // 首屏渲染时间
    firstPaint: performance.getEntriesByType('paint').find(entry => entry.name === 'first-paint')?.startTime || 0,
    // 首次内容ful绘制时间
    firstContentfulPaint: performance.getEntriesByType('paint').find(entry => entry.name === 'first-contentful-paint')?.startTime || 0,
    // 组件渲染时间
    componentRenderTimes: data.measures
      .filter(measure => measure.name.includes('component'))
      .map(measure => ({
        name: measure.name,
        duration: measure.duration
      })),
    // 路由切换时间
    routeTransitionTimes: data.measures
      .filter(measure => measure.name.includes('route'))
      .map(measure => ({
        name: measure.name,
        duration: measure.duration
      })),
    // 慢资源(加载时间超过 1s 的资源)
    slowResources: data.resources
      .filter(resource => resource.duration > 1000)
      .map(resource => ({
        name: resource.name,
        duration: resource.duration
      })),
    // 长任务数量
    longTaskCount: data.longTasks.length,
    // 内存使用情况
    memoryUsage: data.memory ? {
      total: Math.round(data.memory.totalJSHeapSize / 1024 / 1024),
      used: Math.round(data.memory.usedJSHeapSize / 1024 / 1024),
      limit: Math.round(data.memory.jsHeapSizeLimit / 1024 / 1024)
    } : null
  }
  
  return analysis
}

// 定期收集和分析性能数据
function startPerformanceMonitoring() {
  setInterval(() => {
    const data = collectPerformanceData()
    const analysis = analyzePerformanceData(data)
    console.log('性能分析报告:', analysis)
    
    // 可以将分析结果发送到监控服务器
    // reportPerformanceAnalysis(analysis)
  }, 30000) // 每 30 秒收集一次
}

export { collectPerformanceData, analyzePerformanceData, startPerformanceMonitoring }

10. Web Vitals 监控

监控 Core Web Vitals(核心 Web 指标):

// src/utils/webVitals.ts
import { onCLS, onFID, onFCP, onLCP, onTTFB } from 'web-vitals'

function sendToAnalytics(metric: any) {
  console.log('Web Vitals 指标:', metric)
  
  // 可以将指标发送到分析服务器
  // navigator.sendBeacon('/api/web-vitals', JSON.stringify(metric))
}

// 启动 Web Vitals 监控
function startWebVitalsMonitoring() {
  onCLS(sendToAnalytics) // 累积布局偏移
  onFID(sendToAnalytics) // 首次输入延迟
  onFCP(sendToAnalytics) // 首次内容ful绘制
  onLCP(sendToAnalytics) // 最大内容ful绘制
  onTTFB(sendToAnalytics) // 首字节时间
}

export default startWebVitalsMonitoring

最佳实践

1. 只在生产环境监控关键指标

在开发环境可以监控所有指标,但在生产环境只监控关键指标,避免性能开销:

if (import.meta.env.PROD) {
  startPerformanceMonitoring()
  startWebVitalsMonitoring()
}

2. 合理设置监控频率

避免过于频繁的监控,合理设置监控间隔:

// 每 30 秒收集一次性能数据
setInterval(collectPerformanceData, 30000)

// 每 5 秒监控一次内存
setInterval(monitorMemory, 5000)

3. 优先监控用户体验指标

优先监控与用户体验直接相关的指标:

  • 首次内容ful绘制(FCP)
  • 最大内容ful绘制(LCP)
  • 首次输入延迟(FID)
  • 累积布局偏移(CLS)
  • 路由切换时间
  • 组件渲染时间

4. 结合 RUM(真实用户监控)

将前端性能监控与 RUM 系统结合,收集真实用户的性能数据:

function sendPerformanceData(data: any) {
  // 使用 navigator.sendBeacon 发送数据(不会阻塞页面卸载)
  navigator.sendBeacon('/api/performance', JSON.stringify(data))
}

5. 性能数据可视化

将性能数据可视化,便于分析和监控:

<!-- src/components/PerformanceDashboard.vue -->
<template>
  <div class="performance-dashboard">
    <h2>性能监控面板</h2>
    
    <div class="metrics">
      <div class="metric">
        <h3>页面加载时间</h3>
        <p>{{ analysis.loadTime }}ms</p>
      </div>
      <div class="metric">
        <h3>首屏渲染</h3>
        <p>{{ analysis.firstContentfulPaint }}ms</p>
      </div>
      <div class="metric">
        <h3>当前帧率</h3>
        <p>{{ currentFPS }} FPS</p>
      </div>
      <div class="metric">
        <h3>内存使用</h3>
        <p>{{ analysis.memoryUsage?.used }}/{{ analysis.memoryUsage?.total }}MB</p>
      </div>
    </div>
    
    <div class="charts">
      <!-- 可以使用 Chart.js 等库绘制性能图表 -->
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { collectPerformanceData, analyzePerformanceData } from '@/utils/performanceCollector'
import fpsMonitor from '@/utils/fpsMonitor'

const analysis = ref({})
const currentFPS = ref(0)
let updateInterval: number | null = null

onMounted(() => {
  // 启动帧率监控
  fpsMonitor.start()
  
  // 定期更新性能数据
  updateInterval = window.setInterval(() => {
    const data = collectPerformanceData()
    analysis.value = analyzePerformanceData(data)
  }, 5000)
})

onBeforeUnmount(() => {
  // 停止监控
  fpsMonitor.stop()
  if (updateInterval) {
    clearInterval(updateInterval)
  }
})
</script>

常见问题和解决方案

1. 监控代码影响性能

问题:添加监控代码后,应用性能下降

解决方案

  • 只在生产环境监控关键指标
  • 合理设置监控频率
  • 使用轻量化的监控方式
  • 避免在主线程上执行复杂的分析逻辑

2. 性能数据不准确

问题:收集到的性能数据不准确

解决方案

  • 确保在正确的时机创建和清除标记
  • 避免在监控代码中引入额外的延迟
  • 考虑浏览器兼容性问题
  • 使用多个指标交叉验证

3. 数据量过大

问题:收集的性能数据量过大,影响网络性能

解决方案

  • 只收集关键指标
  • 对数据进行采样(如只收集 10% 用户的数据)
  • 压缩数据后再发送
  • 使用 navigator.sendBeacon() 发送数据

4. 浏览器兼容性问题

问题:某些浏览器不支持 Performance API 的某些特性

解决方案

  • 检查 API 支持情况
  • 提供降级方案
  • 使用 polyfill
if ('performance' in window && 'measure' in window.performance) {
  // 使用 Performance API
} else {
  // 降级处理
  console.warn('Performance API 不支持')
}

进阶学习资源

  1. 官方文档

  2. 工具和库

  3. 性能优化指南

  4. 监控工具

实践练习

练习 1:基础性能监控

  1. 初始化一个 Vue 3 项目
  2. 集成 Performance API 基础功能
  3. 监控页面加载时间和资源加载时间
  4. 实现性能数据收集和分析

练习 2:组件性能监控

  1. 实现一个复杂的 Vue 组件
  2. 使用 Performance API 监控组件的渲染性能
  3. 分析组件的性能瓶颈
  4. 优化组件性能并验证效果

练习 3:路由切换性能监控

  1. 配置 Vue Router
  2. 使用导航守卫监控路由切换性能
  3. 实现路由切换动画
  4. 优化路由切换性能

练习 4:Web Vitals 监控

  1. 集成 web-vitals 库
  2. 监控 Core Web Vitals 指标
  3. 实现性能数据可视化
  4. 分析并优化 Web Vitals 指标

练习 5:综合性能监控系统

  1. 实现一个完整的性能监控系统
  2. 监控页面加载、组件渲染、路由切换、异步请求等
  3. 实现性能数据的收集、分析和可视化
  4. 集成到 Vue 3 应用中

总结

Performance API 是监控和优化 Vue 3 应用性能的强大工具。通过合理使用 Performance API,你可以深入了解应用的性能状况,识别性能瓶颈,优化用户体验。从基础的性能标记和测量到高级的 Web Vitals 监控,Performance API 提供了全面的性能监控解决方案。结合 Vue 3 的组合式 API,你可以轻松实现组件级别的性能监控,构建高性能的 Vue 3 应用。

下一集我们将学习 Vue 3 与 Lighthouse 性能审计,敬请期待!

« 上一篇 122-vue3-cypress-advanced-e2e-testing 下一篇 » Vue 3与Lighthouse性能审计 - 全面性能评估与优化解决方案