Vue3 性能优化的核心手段

春秋大王2025-10-105 次阅读
Vue3 性能优化的核心手段


Vue3 的性能优化需从 “渲染效率”“资源体积”“运行时开销” 三个维度切入,结合框架特性与工程化工具,可实现 30% 以上的性能提升。以下是实际项目中高频使用的优化方案及代码实现:​

(1)异步组件与 Suspense:减少初始加载时间​

对于大型组件(如复杂表单、图表),使用异步组件可将其拆分到单独的 JS 文件中,仅在需要时加载,降低首屏 JS 体积。配合 Vue3 的<Suspense>组件,还能优雅处理加载状态与错误捕获。

<!-- 父组件:使用异步组件与Suspense -->
<template>
  <div class="dashboard">
    <h3>数据仪表盘</h3>
    <!-- Suspense:包裹异步组件,管理加载/错误状态 -->
    <Suspense>
      <!-- 异步加载的复杂组件:仅当用户切换到该面板时加载 -->
      <AsyncChartComponent v-if="showChart" :data="chartData" />
      <!-- 加载中状态(fallback插槽) -->
      <template #fallback>
        <div class="loading">
          <span class="spinner"></span>
          <p>图表加载中...</p>
        </div>
      </template>
    </Suspense>
    <button @click="toggleChart"> {{ showChart ? '隐藏图表' : '显示图表' }} </button>
  </div>
</template>

<script setup>
import { ref, defineAsyncComponent } from 'vue'
// 1. 异步引入组件:使用defineAsyncComponent,返回组件工厂函数
// 打包时会将AsyncChartComponent拆分为单独的chunk(如AsyncChartComponent.123.js)
const AsyncChartComponent = defineAsyncComponent(() => 
  import('./AsyncChartComponent.vue')
)

// 模拟图表数据(实际从接口获取)
const chartData = ref({
  sales: [120, 200, 150, 250, 180],
  dates: ['1月', '2月', '3月', '4月', '5月']
})
const showChart = ref(false)

// 切换图表显示状态
const toggleChart = () => {
  showChart.value = !showChart.value
}
</script>

<style scoped>
.dashboard {
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
  max-width: 800px;
  margin: 20px auto;
}
.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 50px;
  color: #666;
}
.spinner {
  display: inline-block;
  width: 24px;
  height: 24px;
  border: 3px solid #eee;
  border-top-color: #42b983;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-right: 10px;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
button {
  margin-top: 20px;
  padding: 8px 16px;
  background: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>

(2)v-memo:缓存 DOM 避免不必要重渲染​

v-memo是 Vue3 新增的指令,通过指定 “依赖数组”,仅当依赖变化时才重新渲染 DOM 节点,适合列表、表格等高频更新场景,可减少 50% 以上的 DOM 操作。

<template>​
  <!-- 场景:带筛选功能的商品列表 -->​
  padding: 15px;​
  border-bottom: 1px solid #eee;​
}​
.product-img {​
  width: 80px;​
  height: 60px;​
  object-fit: cover;​
  border-radius: 4px;​
  margin-right: 15px;​
}​
.product-info {​
  flex: 1;​
}​
.price {​
  color: #ff4444;​
  margin: 5px 0;​
}​
.hot-tag {​
  background: #ff4444;​
  color: white;​
  font-size: 12px;​
  padding: 2px 8px;​
  border-radius: 12px;​
}​
</style>

(3)长列表虚拟滚动:降低 DOM 节点数量​

当列表数据超过 1000 条时,直接渲染所有 DOM 节点会导致页面卡顿。使用vue-virtual-scroller实现 “可视区域渲染”,仅渲染当前可见的列表项,DOM 节点数量从数千降至数十,大幅提升流畅度。

<template>
  <div class="long-list-container">
    <h3>10万条数据的长列表(虚拟滚动)</h3>
    <!-- RecycleScroller:虚拟滚动核心组件 -->
    <RecycleScroller
      class="scroller"
      :items="longList"  <!-- 完整数据列表 -->
      :item-size="60"   <!-- 每个列表项的固定高度(关键参数) -->
      key-field="id"    <!-- 数据的唯一标识字段 -->
      v-slot="{ item }"  <!-- 插槽:渲染单个列表项 -->
    >
      <div class="list-item">
        <span class="item-index">{{ item.id }}.</span>
        <span class="item-content">{{ item.content }}</span>
      </div>
    </RecycleScroller>
  </div>
</template>

<script setup>
import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'  // 引入默认样式
import { ref } from 'vue'

// 模拟10万条数据(实际项目从接口分页获取,此处为演示)
const longList = ref(Array.from({ length: 100000 }, (_, i) => ({
  id: i + 1,
  content: `列表项内容 ${i + 1} - 这是一条模拟的长列表数据`
})))
</script>

<style scoped>
.long-list-container {
  max-width: 800px;
  margin: 20px auto;
  padding: 0 20px;
}
.scroller {
  height: 500px;  /* 固定滚动容器高度(必须) */
  overflow: auto;  /* 显示滚动条 */
  border: 1px solid #eee;
  border-radius: 8px;
}
.list-item {
  height: 60px;  /* 与item-size保持一致 */
  line-height: 60px;
  padding: 0 20px;
  border-bottom: 1px solid #f5f5f5;
  display: flex;
  align-items: center;
}
.item-index {
  color: #42b983;
  font-weight: bold;
  margin-right: 15px;
  width: 40px;
  text-align: right;
}
.item-content {
  color: #333;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>