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 router5. 异步操作性能监控
监控异步操作(如 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 api6. 长任务监控
监控长任务(执行时间超过 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 observer7. 内存监控
使用 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 fpsMonitor9. 性能数据收集和分析
收集和分析性能数据:
// 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 不支持')
}进阶学习资源
官方文档:
工具和库:
- web-vitals - Google 官方 Web Vitals 库
- performance.now()
- PerformanceObserver
性能优化指南:
监控工具:
实践练习
练习 1:基础性能监控
- 初始化一个 Vue 3 项目
- 集成 Performance API 基础功能
- 监控页面加载时间和资源加载时间
- 实现性能数据收集和分析
练习 2:组件性能监控
- 实现一个复杂的 Vue 组件
- 使用 Performance API 监控组件的渲染性能
- 分析组件的性能瓶颈
- 优化组件性能并验证效果
练习 3:路由切换性能监控
- 配置 Vue Router
- 使用导航守卫监控路由切换性能
- 实现路由切换动画
- 优化路由切换性能
练习 4:Web Vitals 监控
- 集成 web-vitals 库
- 监控 Core Web Vitals 指标
- 实现性能数据可视化
- 分析并优化 Web Vitals 指标
练习 5:综合性能监控系统
- 实现一个完整的性能监控系统
- 监控页面加载、组件渲染、路由切换、异步请求等
- 实现性能数据的收集、分析和可视化
- 集成到 Vue 3 应用中
总结
Performance API 是监控和优化 Vue 3 应用性能的强大工具。通过合理使用 Performance API,你可以深入了解应用的性能状况,识别性能瓶颈,优化用户体验。从基础的性能标记和测量到高级的 Web Vitals 监控,Performance API 提供了全面的性能监控解决方案。结合 Vue 3 的组合式 API,你可以轻松实现组件级别的性能监控,构建高性能的 Vue 3 应用。
下一集我们将学习 Vue 3 与 Lighthouse 性能审计,敬请期待!