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>
