最近在写聊天工具的会话列表部分,发现一个问题,当会话出现频繁更新的时候,会让列表渲染的非常频繁。
虽然影响很小,但是做一流工具的态度还是要有的,所以研究研究怎么把v-for的渲染更新频率降低。
在vue中,使用v-for指令进行渲染的时候,vue会尽可能重用现有的DOM元素,而不是每次全部重新渲染,这就是 就地更新 。
<template v-for="talk in talkList">
<component :is="talk.sendUserId === selfId ? 'MeMsg' : 'OtherMsg'" :msg-record="talk"></component>
</template>
上述代码当 talkList 出现 push 新对象的时候,被循环的动态组件就会发生重新渲染,想要尽可能降低渲染次数的方法有几种。
1. 给列表项一个唯一的key
为每个列表项绑定一个唯一的key属性,vue框架能够高效的识别出是哪个项发生了修改,只重现渲染特定的项,而不会出现全部项重新渲染。
<template v-for="talk in talkList" :key="talk.id">
<component :is="talk.sendUserId === selfId ? 'MeMsg' : 'OtherMsg'" :msg-record="talk"></component>
</template>
2. 通过对象的响应性更新
当数组中列表项出现增删的时候,如果数组是在data中定义的,那么 talkList 数组和内部对象也会变成响应式的,当出现更新的时候列表项也会随之重新渲染。
// 新增
this.talkList.push({ id: 3, sendUserId: 'user3', message: 'Another message' });
this.talkList.unshift({ id: 3, sendUserId: 'user3', message: 'Another message' });
// 替换
this.talkList.splice(index, 1, { id: 1, sendUserId: 'user1', message: 'Replaced message' });
如果需要向 talkList 动态添加新属性,或者更新对象中现有的属性,并确保其响应性,可以通过 $set 方法更新。
// 假设你想更新id为1的对象的消息内容
let index = this.talkList.findIndex(talk => talk.id === 1);
if (index !== -1) {
// 使用this.$set确保更改是响应式的
this.$set(this.talkList[index], 'message', 'Updated message');
}
3. 通过生命周期钩子
export default {
props: ["msgRecord"],
beforeUpdate(){
// 判断是否发生更新,没有变化可以阻止更新
},
watch: {
msgRecord(newVal, oldVal){
// 根据数据变化的情况决定是否更新
}
}
}
4. 使用keep-alive缓存
如果出现频繁的显隐操作可以使用 keep-alive 包裹列表项,将列表项状态进行缓存,显示时就不会再重新渲染。(与当前需求不符合,只是提供一种思路)
5. 使用v-memo指令
在 vue3 中存在一个新的指令 v-memo,通过该指令判断当数据发生变化时才重新渲染该组件。(这是性价比最高的一种方案)
<template v-for="talk in talkList" :key="talk.id" v-memo="[talk]">
<component :is="talk.sendUserId === selfId ? 'MeMsg' : 'OtherMsg'" :msg-record="talk"></component>
</template>
结论
综上,如果在 vue2 中,性能要求不是非常“BT”的情况下,直接绑定 key 即可,有要求可以考虑使用生命周期钩子进行操作。vue3中直接无脑上 v-memo 即可。
原文链接:https://juejin.cn/post/7457551678210326582 作者:李剑一