v-for 中 key 的作用

理解 Vue 列表渲染中 key 属性对 Diff 算法和节点复用的影响

问题

在 Vue 的 v-for 中,key 属性有什么作用?为什么推荐使用唯一标识而不是索引?

解答

key 的作用

key 是 Vue 识别 VNode 的唯一标识,用于在 Diff 算法中判断新旧节点是否可以复用。

没有 key 的情况

Vue 采用”就地更新”策略,按索引顺序复用节点:

// 旧列表
['A', 'B', 'C']

// 新列表(在头部插入 D)
['D', 'A', 'B', 'C']

// 没有 key 时的更新过程:
// 索引 0: A -> D (更新内容)
// 索引 1: B -> A (更新内容)
// 索引 2: C -> B (更新内容)
// 索引 3: 创建 C
// 结果:更新了 3 次,创建了 1 次

有 key 的情况

Vue 通过 key 精确匹配可复用节点:

// 旧列表
[{ id: 1, text: 'A' }, { id: 2, text: 'B' }, { id: 3, text: 'C' }]

// 新列表(在头部插入 D)
[{ id: 4, text: 'D' }, { id: 1, text: 'A' }, { id: 2, text: 'B' }, { id: 3, text: 'C' }]

// 有 key 时的更新过程:
// key=1 的节点复用,移动位置
// key=2 的节点复用,移动位置
// key=3 的节点复用,移动位置
// key=4 的节点是新的,创建
// 结果:移动了 3 次,创建了 1 次(DOM 移动比更新内容开销小)

代码示例

<template>
  <div>
    <!-- 错误:使用 index 作为 key -->
    <div v-for="(item, index) in list" :key="index">
      <input v-model="item.value" />
      {{ item.text }}
    </div>

    <!-- 正确:使用唯一标识作为 key -->
    <div v-for="item in list" :key="item.id">
      <input v-model="item.value" />
      {{ item.text }}
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      list: [
        { id: 1, text: 'A', value: '' },
        { id: 2, text: 'B', value: '' },
        { id: 3, text: 'C', value: '' }
      ]
    }
  },
  methods: {
    // 在头部插入时,用 index 作为 key 会导致输入框内容错位
    addFirst() {
      this.list.unshift({ id: Date.now(), text: 'New', value: '' })
    }
  }
}
</script>

为什么不用 index 作为 key

// 原列表
items: [
  { id: 1, text: 'A' },  // index: 0
  { id: 2, text: 'B' },  // index: 1
  { id: 3, text: 'C' }   // index: 2
]

// 删除 B 后
items: [
  { id: 1, text: 'A' },  // index: 0 (key 不变)
  { id: 3, text: 'C' }   // index: 1 (key 从 2 变成 1)
]

// 用 index 作为 key:Vue 认为 index=1 的节点内容从 B 变成了 C
// 用 id 作为 key:Vue 知道 id=2 的节点被删除,id=3 的节点保持不变

关键点

  • key 是 VNode 的唯一标识,帮助 Diff 算法识别节点
  • 没有 key 时采用就地更新策略,可能导致状态错乱
  • key 时可以精确复用和移动节点,减少 DOM 操作
  • 不要用 index 作为 key,列表变化时会导致错误复用
  • 推荐使用数据的唯一标识(如 id)作为 key