前言
这两天在阅读Vue3官方文档时,发现在Vue3.2版本中,新增了一个名为v-memo的指令,这让我想到了react中的useMemo。在仔细阅读了文档描述后发现,这果然是一个用于页面性能优化的指令。
什么是v-memo?
官方文档中是这样描述该指令的:
缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过.
举个例子:
<div v-memo="[valueA, valueB]">
...
</div>
当组件重新渲染,如果 valueA
和 valueB
都保持不变,这个 <div>
及其子项的所有更新都将被跳过。实际上,甚至虚拟 DOM 的 vnode 创建也将被跳过,因为缓存的子树副本可以被重新使用。
正确指定缓存数组很重要,否则应该生效的更新可能被跳过。v-memo
传入空依赖数组 (v-memo="[]"
) 将与 v-once
效果相同。
简单来说,v-memo
接受一个依赖的数组,依赖的数组变化,v-memo
所对应的 DOM
包括子集将会重新渲染,反过来说,如果依赖的数组不变,即使整组件重新渲染了,v-memo
所对应的 DOM
包括子集更新都将被跳过。
如果用一个空数组调用 v-memo
相当于使用 v-once
,只会渲染该部分组件一次。
可以说,v-memo
相当于一个作用对象是DOM
的计算属性。
使用
一、手动控制节点更新
通过使用v-memo
,可以手动控制节点是否更新
下面这个例子中有三个计数器,但只有在被依赖的 Subscribers
变化时, 子节点才会重新渲染。
<script setup>
import { ref } from 'vue'
const subscribers = ref(4000)
const views = ref(10000)
const likes = ref(3000)
</script>
<template>
<div>
<div v-memo="[subscribers]">
<p>Subscribers: {{ subscribers }}</p>
<p>Views: {{ views }}</p>
<p>Likes: {{ likes }}</p>
</div>
<button @click="subscribers++">Subscribers++</button>
<button @click="views++">Views++</button>
<button @click="likes++">Likes++</button>
<div>
<p>Current state:</p>
<p>Subscribers: {{ subscribers }}</p>
<p>Views: {{ views }}</p>
<p>Likes: {{ likes }}</p>
</div>
</div>
</template>
在修改子节点依赖的views,likes时,子节点 p 将不会重新渲染,但只要更新subscribers
,div 中的内容即会重新渲染。
二、与v-for配合使用
v-memo
常用于需要渲染大量v-for
列表的时候
下面用一个例子进行演示:
<script setup>
import { ref } from 'vue'
// 被选中的值
const selected = ref(0);
// 造的列表数据
const list = ref(
Array.from({ length: 1001 }, (_, index) => {
return {
id: index,
name: `test${index}`,
};
}),
);
// 选中点击方法
const onClickSelect = (id) => {
selected.value = id;
// 记录开始时间
console.time()
nextTick(()=> {
// 记录结束时间
console.timeEnd()
})
}
</script>
<template>
<div>
<div v-for="item in list" @click="onClickSelect(item.id)" v-memo="[item.id == selected]">
<p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p>
</div>
</div>
</template>
当组件的 selected
状态改变,默认会重新创建大量的 vnode,尽管绝大部分都跟之前是一模一样的。
在点击 v-for
生成列表中任意一项时,selected
值会发生改变。
在不使用 v-memo
时,可以看到,控制台输出的渲染时间为
在添加上
v-memo
后,渲染时间为
可以看出,有了一些较小的性能提升
再将数据增加到一万条时,不使用 v-memo
的平均 DOM
更新时间大约为 55ms+
使用 v-memo
的平均 DOM
更新时间大约为 30ms+
总结
可以看到,v-memo
在数据较多的情况下,是可以通过手动操作对页面进行优化的,但使用场景大多在中型或大型项目中。
通过以上例子,也正验证了Vue官方文档中对该指令的描述:用于性能至上场景中的微小优化。
该文章借鉴了这位大佬的文章