3.Vue3 性能优化 - 标准答案

考察点:性能调优、内存管理、渲染优化、实战能力

场景描述: 优化一个 Vue3 的数据可视化大屏项目:

  • 实时展示 5000+ 条设备状态数据(每秒更新 50-100 条)
  • 页面包含多个图表组件(ECharts)、表格、卡片
  • 首屏加载时间 8秒+,滚动时有明显卡顿
  • 内存占用持续增长,1小时后页面变慢

问题

  1. 渲染优化:除虚拟滚动外,Vue3 还有哪些优化大数据列表的手段?
  2. 组件优化:如何优化频繁更新的图表组件?
  3. 内存泄漏:如何排查并解决 Vue3 应用中的内存泄漏?
  4. 首屏优化:针对 8秒+ 的首屏时间,有哪些优化方案?

核心手段

// 1. shallowRef - 浅层响应式(只追踪 .value 变化)
import { shallowRef } from 'vue'

const largeList = shallowRef([/* 5000+ 条数据 */])

// 更新数据时手动触发更新
function updateList(newData) {
  largeList.value = [...newData]  // 整体替换才会触发更新
}

// 2. shallowReactive - 只代理第一层
const state = shallowReactive({
  items: [/* 大数组 */]
})

// 3. v-memo - 缓存组件渲染结果
<template>
  <div v-for="item in list" :key="item.id" v-memo="[item.status]">
    <!-- 只有 item.status 变化时才重新渲染 -->
    <DeviceCard :data="item" />
  </div>
</template>

// 4. v-once - 只渲染一次
<div v-once>
  <!-- 静态内容,永不更新 -->
  <StaticChart />
</div>

// 5. 虚拟滚动
import { useVirtualList } from '@vueuse/core'

const { list: virtualList, containerProps, wrapperProps } = useVirtualList(
  largeList,
  { itemHeight: 80, overscan: 10 }
)

图表组件防抖节流

import { debounce } from 'lodash-es'
import * as echarts from 'echarts/core'

export default {
  setup() {
    const chartRef = ref()
    let chartInstance = null
    
    // 防抖更新图表
    const updateChart = debounce((data) => {
      if (!chartInstance) {
        chartInstance = echarts.init(chartRef.value)
      }
      
      chartInstance.setOption({
        series: [{ data }]
      })
    }, 300)
    
    // WebSocket 数据流
    onMounted(() => {
      const ws = new WebSocket('ws://...')
      ws.onmessage = (event) => {
        const newData = JSON.parse(event.data)
        updateChart(newData)  // 自动防抖
      }
      
      onUnmounted(() => {
        ws.close()
        chartInstance?.dispose()  // 释放图表实例
      })
    })
    
    return { chartRef }
  }
}

组件缓存

<template>
  <!-- 缓存不活跃的标签页 -->
  <KeepAlive :max="5">
    <component :is="currentTab" />
  </KeepAlive>
</template>

常见泄漏点

// ❌ 问题1:定时器未清理
export default {
  setup() {
    const timer = setInterval(() => {
      // ...
    }, 1000)
    
    // ✅ 解决:清理定时器
    onUnmounted(() => {
      clearInterval(timer)
    })
  }
}

// ❌ 问题2:全局事件未解绑
export default {
  setup() {
    const handleResize = () => { /* ... */ }
    
    onMounted(() => {
      window.addEventListener('resize', handleResize)
    })
    
    // ✅ 解决:解绑事件
    onUnmounted(() => {
      window.removeEventListener('resize', handleResize)
    })
  }
}

// ❌ 问题3:第三方库实例未销毁
export default {
  setup() {
    let chart = null
    
    onMounted(() => {
      chart = echarts.init(chartRef.value)
    })
    
    // ✅ 解决:销毁实例
    onUnmounted(() => {
      chart?.dispose()
      chart = null
    })
  }
}

排查工具

  1. Chrome DevTools - Memory
    • 录制堆快照(Heap Snapshot)
    • 对比快照找泄漏点
    • 查看 Detached DOM nodes
  2. Vue DevTools
    • 查看组件树
    • 检查未清理的组件实例

优化策略

// 1. 路由懒加载
const routes = [
  {
    path: '/dashboard',
    component: () => import('./views/Dashboard.vue')  // 懒加载
  }
]

// 2. 组件懒加载
<script setup>
import { defineAsyncComponent } from 'vue'

const HeavyChart = defineAsyncComponent(() =>
  import('./components/HeavyChart.vue')
)
</script>

// 3. 骨架屏
<template>
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <SkeletonScreen />
    </template>
  </Suspense>
</template>

// 4. 代码分割(Vite)
// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'echarts': ['echarts'],
          'vendor': ['vue', 'pinia']
        }
      }
    }
  }
}

// 5. 图片懒加载
import { useIntersectionObserver } from '@vueuse/core'

const imgRef = ref()
const { stop } = useIntersectionObserver(
  imgRef,
  ([{ isIntersecting }]) => {
    if (isIntersecting) {
      // 加载图片
      imgRef.value.src = realSrc
      stop()
    }
  }
)

  • 使用 Lighthouse 分析性能指标
  • Chrome DevTools Performance 录制加载过程
  • 检查 Network 面板的资源加载时间
  • 使用 Vue DevTools 查看组件渲染时间
  • 监控 Memory 面板的内存占用趋势
  • 检查 Console 警告和错误

  1. 大数据列表:虚拟滚动 + shallowRef
  2. 频繁更新:防抖节流 + v-memo
  3. 图表组件:缓存实例 + 按需更新
  4. 首屏加载:路由懒加载 + 骨架屏 + 代码分割
  5. 内存管理:及时清理定时器、事件、第三方实例
  6. 监控优化:使用性能监控工具持续优化