Vue/React的一种安全获取key的方法

3,684 阅读2分钟

背景

我们渲染列表的时候,都知道要用数据的id值作为key,避免潜在的问题。但是总有一些情况,我们就是拿不到合适的key值,那么这种情况该怎么办呢?

先说2个不太合适的方案

  1. 使用index作为key
  2. 使用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是稳定的,且不会污染数据。