📋 概述
v-memo 是 Vue 3.2+ 新增的指令,用于缓存模板的一部分 ,只有当指定的依赖项发生变化时,才会重新渲染该部分。相比 v-if,v-memo 更加精细化控制渲染时机,可以显著提升列表渲染性能。
🔰 基本用法
语法
vue
<template>
<div v-memo="[依赖项1, 依赖项2, ...]">
<!-- 内容 -->
</div>
</template>
v-memo 接收一个数组作为参数,数组中包含需要监听的响应式依赖。只有当数组中的任意一项发生变化时,被包裹的内容才会重新渲染。
简单示例
vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
const name = ref('张三')
</script>
<template>
<!-- 只有 name 变化时重新渲染 -->
<div v-memo="[name]">
<p>用户名: {{ name }}</p>
<p>计数: {{ count }}</p>
</div>
</template>
效果:
- 当
name变化时,整个 div 会重新渲染 - 当
count变化时,div 不会重新渲染(因为 count 不在依赖数组中)
🔄 v-memo vs v-if
v-if 的问题
vue
<!-- v-if 控制整个元素的显示/隐藏 -->
<div v-if="isVisible">
<p>姓名: {{ name }}</p>
<p>年龄: {{ age }}</p>
<p>城市: {{ city }}</p>
</div>
v-if="false" 会完全移除 DOM 元素,后续需要重新创建。
v-memo 的优势
vue
<!-- v-memo 保留 DOM,仅控制是否更新 -->
<div v-memo="[name, age]">
<p>姓名: {{ name }}</p>
<p>年龄: {{ age }}</p>
<p>城市: {{ city }}</p>
</div>
- DOM 元素始终保留
- 只有
name或age变化时,才重新渲染内容 city变化不会触发重新渲染
对比表
| 特性 | v-if | v-memo |
|---|---|---|
| DOM 操作 | 创建/销毁 DOM | 保留 DOM,仅更新内容 |
| 性能开销 | 频繁切换时开销大 | 适合内容频繁变化的场景 |
| 条件变化 | 完全不渲染 | 保持渲染,仅更新数据 |
| 适用场景 | 条件性显示/隐藏 | 条件性更新内容 |
💡 实际应用场景
场景一:大列表渲染优化
vue
<script setup>
import { ref } from 'vue'
const items = ref([
{ id: 1, name: '商品A', price: 100, count: 2 },
{ id: 2, name: '商品B', price: 200, count: 1 },
{ id: 3, name: '商品C', price: 300, count: 3 },
])
const isVip = ref(false)
function updateCount(id) {
const item = items.value.find(i => i.id === id)
if (item) item.count++
}
</script>
<template>
<div class="cart">
<h2>购物车</h2>
<div
v-for="item in items"
:key="item.id"
v-memo="[item.count, isVip]"
class="item"
>
<span>{{ item.name }}</span>
<span>单价: ¥{{ item.price }}</span>
<span>数量: {{ item.count }}</span>
<span>小计: ¥{{ item.price * item.count }}</span>
<span v-if="isVip">VIP折扣: -¥{{ item.price * 0.1 }}</span>
<button @click="updateCount(item.id)">+1</button>
</div>
</div>
</template>
优化效果:
- 只监听
item.count和isVip的变化 - 当其他商品变化时,当前商品不会重新渲染
- 显著减少不必要的重渲染
场景二:表格行优化
vue
<script setup>
import { ref } from 'vue'
const rows = ref([
{ id: 1, name: '项目A', status: '进行中', progress: 50 },
{ id: 2, name: '项目B', status: '已完成', progress: 100 },
{ id: 3, name: '项目C', status: '待开始', progress: 0 },
])
function updateProgress(id) {
const row = rows.value.find(r => r.id === id)
if (row) row.progress = Math.min(100, row.progress + 10)
}
</script>
<template>
<table>
<thead>
<tr>
<th>项目名</th>
<th>状态</th>
<th>进度</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="row in rows"
:key="row.id"
v-memo="[row.status, row.progress]"
>
<td>{{ row.name }}</td>
<td>{{ row.status }}</td>
<td>{{ row.progress }}%</td>
<td>
<button @click="updateProgress(row.id)">推进</button>
</td>
</tr>
</tbody>
</table>
</template>
🎯 性能优化技巧
1. 精确依赖数组
vue
<!-- ❌ 错误:依赖过多,命中率低 -->
<div v-memo="[item.name, item.price, item.count, item.discount, ...]">
<!-- ✅ 正确:只监听真正影响渲染的依赖 -->
<div v-memo="[item.price, item.count]">
<p>总价: ¥{{ item.price * item.count }}</p>
</div>
2. 避免不必要的 v-memo
vue
<!-- ❌ 简单内容不需要 v-memo -->
<span v-memo="[name]">{{ name }}</span>
<!-- ✅ 复杂渲染逻辑才需要 -->
<div v-memo="[data]">
<ExpensiveComponent :data="data" />
<AnotherHeavyComponent />
</div>
3. 空依赖数组 = v-once
vue
<!-- 只在组件挂载时渲染一次,后续永不更新 -->
<div v-memo="[]">
<StaticContent />
</div>
⚠️ 注意事项
1. 与 v-for 配合
vue
<!-- v-memo 必须在 v-for 内使用 -->
<tr
v-for="item in items"
:key="item.id"
v-memo="[item.status]"
>
<td>{{ item.name }}</td>
<td>{{ item.status }}</td>
</tr>
2. 嵌套使用
vue
<div v-memo="[outerVar]">
<div v-memo="[innerVar]">
<!-- 同时监听 outerVar 和 innerVar -->
{{ outerVar }} - {{ innerVar }}
</div>
</div>
3. 响应式依赖
vue
<script setup>
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
// ✅ ref 和 computed 都可以作为依赖
<div v-memo="[count, doubled]">
{{ count }} * 2 = {{ doubled }}
</div>
</script>
💡 v-memo 工作原理
内部机制
- 依赖追踪:记录模板中使用的响应式变量
- 缓存比较:将当前依赖值与上次渲染时的值进行比较
- 条件渲染:只有依赖变化时才执行更新
与 v-once 的区别
vue
<!-- v-once:只渲染一次,内容永不更新 -->
<div v-once>
{{ staticContent }}
</div>
<!-- v-memo:依赖变化时更新,不变化时跳过 -->
<div v-memo="[dynamicVar]">
{{ dynamicVar }}
</div>
📊 性能对比
| 优化手段 | 作用层级 | 适用场景 | 性能收益 |
|---|---|---|---|
v-memo |
模板级 | 减少不必要的重新渲染 | ⭐⭐⭐⭐ |
computed |
逻辑级 | 缓存计算结果 | ⭐⭐⭐⭐ |
shallowRef |
数据级 | 避免深层响应式开销 | ⭐⭐⭐ |
markRaw |
数据级 | 标记不可响应的大对象 | ⭐⭐⭐ |
| 虚拟列表 | DOM 级 | 处理超长列表 | ⭐⭐⭐⭐⭐ |
⚡ 快速参考
vue
<!-- 基础用法 -->
<div v-memo="[count]">{{ count }}</div>
<!-- 多个依赖 -->
<div v-memo="[name, age, status]">
{{ name }} - {{ age }} - {{ status }}
</div>
<!-- 空数组 = 只渲染一次 -->
<div v-memo="[]">永不更新</div>
<!-- v-for 中使用 -->
<tr v-for="item in items" :key="item.id" v-memo="[item.active]">
<td>{{ item.name }}</td>
</tr>
🎯 总结
v-memo:Vue 3.2+ 提供的模板级缓存指令- 核心功能:基于依赖变化控制是否重新渲染,而非控制显示/隐藏
- 最佳场景:大列表渲染、复杂组件的条件更新
- 配合使用 :与
computed、虚拟列表等配合效果更佳 - 注意事项:避免过度使用,精确依赖数组
合理使用 v-memo 可以显著提升 Vue 应用的渲染性能,特别是在处理大型列表和频繁更新的场景中。
💬 互动时间
你在项目中有用过 v-memo 吗?有什么性能优化的心得?欢迎在评论区分享!
如果这篇文章对你有帮助,欢迎:
- 👍 点赞
- ❤️ 收藏
- 👀 关注
我会持续分享 Vue 3 和前端性能优化的实战技巧~
#Vue3 #前端性能优化 #Composition API #前端开发 #Web开发