背景
我们渲染列表的时候,都知道要用数据的id值作为key,避免潜在的问题。但是总有一些情况,我们就是拿不到合适的key值,那么这种情况该怎么办呢?
先说2个不太合适的方案
- 使用index作为key
- 使用Math.random()作为key
对于第1种方案,在 《什么情况下Vue使用index作为key会出问题》这篇文章中有过分析,简单来说就是如果你要对这个列表进行增删的操作,可能会出现渲染错乱的问题。
对于第2种方案,它虽然解决了第1种方案的问题,但是却带来了其他问题。首先key不稳定,每次列表渲染,列表里的每项的组件都要重新渲染,性能不好。其次,由于每次渲染key都会变,这会严重影响用户交互, 这里我写了一个DEMO,在DEMO中当我们在输入框输入内容时,会异常失去焦点。
再说1种不太完美的方案
既然上面直接把key="Math.random()"
直接写在了模板中,导致我们每次渲染,key都会变。那么我们就换种方式,我们事先处理一下list数据,举个例子:
data() {
return {
// 数据初始化时加上key
list: [{}, {}, {}].map(item => {
item.key = Math.random()
return item
})
}
},
methods: {
// 每次添加项目时,也事先加上key
addItem2List() {
this.list.push({
key: Math.random()
})
}
}
通过这个处理,我们就能固定下key,从而解决上述问题。但是这种方案的问题是,污染了数据。如果后续我们要把这份儿list存到数据库,我们不得不再过滤掉这些key,十分麻烦。
介绍一种安全且方便的获取key的方法
以Vue为例,React同理:
<template>
<div>
<ul>
<li v-for="item in list" :key="getUID(item)"></li>
</ul>
</div>
</template>
<script>
let uid = 0
// WeakMap保证了Map的key可以被及时GC
const Item2UIDMap = new WeakMap()
export default {
data() {
return {
list: [{}, {}, {}]
}
},
methods: {
getUID(item) {
const persistedUID = Item2UIDMap.get(item)
if (!persistedUID) {
Item2UIDMap.set(item, ++uid)
return uid
}
return persistedUID
}
}
}
</script>
这种方式可以保证key是稳定的,且不会污染数据。