Frontend Development 17 min read

Why Using Index as Key in Vue Is Discouraged

Using an array index as the key in Vue list rendering can cause unnecessary DOM re‑creation, performance degradation, and data misalignment, especially when items are inserted, removed, or reordered, so developers should prefer stable, unique identifiers such as IDs, symbols, or UUIDs for keys.

政采云技术
政采云技术
政采云技术
Why Using Index as Key in Vue Is Discouraged

Introduction

In front‑end development, list rendering in frameworks such as Vue requires each item to have a unique key . Many developers mistakenly use the array index as the key without understanding its impact.

Purpose of key

Vue’s virtual DOM uses key as a unique identifier during its diff algorithm, allowing efficient comparison between old and new VNodes.

Role of key in the diff algorithm

The diff process in Vue 3 is performed inside patchChildren . When a key is present, the algorithm follows the keyed‑fragment path; otherwise it falls back to an unkeyed fast path.

if (patchFlag > 0) {
      if (patchFlag & PatchFlags.KEYED_FRAGMENT) {
        /* 对于存在 key 的情况用于 diff 算法 */
        patchKeyedChildren(
          ...
        )
        return
      } else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) {
        /* 对于不存在 key 的情况,直接 patch */
        patchUnkeyedChildren(
          ...
        )
        return
      }
    }

Keyed diff proceeds through four main steps: synchronizing head nodes, synchronizing tail nodes, adding new nodes, and removing excess nodes.

Synchronizing head nodes

//(a b) c
//(a b) d e
/* 从头对比找到有相同的节点 patch,发现不同,立即跳出 */
while (i <= e1 && i <= e2) {
  const n1 = c1[i]
  const n2 = (c2[i] = optimized
        ? cloneIfMounted(c2[i] as VNode)
        : normalizeVNode(c2[i]))
  /* 判断 key,type 是否相等 */
  if (isSameVNodeType(n1, n2)) {
    patch(
      ...
    )
  } else {
    break
  }
  i++
}

Synchronizing tail nodes

//a (b c)
//d e (b c)
/* 如果第一步没有 patch 完,立即,从后往前开始 patch  如果发现不同立即跳出循环 */
while (i <= e1 && i <= e2) {
  const n1 = c1[e1]
  const n2 = (c2[e2] = optimized
        ? cloneIfMounted(c2[e2] as VNode)
        : normalizeVNode(c2[e2]))
  if (isSameVNodeType(n1, n2)) {
    patch(
      ...
    )
  } else {
    break
  }
  e1--
  e2--
}

Adding new nodes

//(a b)
//(a b) c
//i = 2, e1 = 1, e2 = 2
//(a b)
//c (a b)
//i = 0, e1 = -1, e2 = 0
/* 如果新的节点大于老的节点数 对于剩下的节点全部以新的 vnode 处理(这种情况说明已经 patch 完相同的 vnode) */
if (i > e1) {
  if (i <= e2) {
    const nextPos = e2 + 1
    const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor
    while (i <= e2) {
      patch( /* 创建新的节点 */
        ...
      )
      i++
    }
  }
}

Removing excess nodes

//i > e2
//(a b) c
//(a b)
//i = 2, e1 = 2, e2 = 1
//a (b c)
//(b c)
//i = 0, e1 = 0, e2 = -1
else if (i > e2) {
  while (i <= e1) {
    unmount(c1[i], parentComponent, parentSuspense, true)
    i++
  }
}

Longest Increasing Subsequence (LIS)

When both old and new lists contain many unordered nodes, Vue 3 finds the longest increasing subsequence of indices to minimise moves. Elements that belong to the LIS stay in place; the rest are moved, inserted, or removed.

这里引入一个概念,叫最长递增子序列。
官方解释:在一个给定的数组中,找到一组递增的数值,并且长度尽可能的大。

const arr = [10, 9, 2, 5, 3, 7, 101, 18]
=> [2, 3, 7, 18]

Why Not to Use Index as key

Performance cost

When the list order changes, using the index forces Vue to treat every item as a new VNode, causing all DOM nodes to be recreated. The following example demonstrates that inserting a single element triggers re‑rendering of all three items.

<template>
  <div class="hello">
    <ul>
      <li v-for="(item,index) in studentList" :key="index">{{item.name}}</li>
    </ul>
    <button @click="addStudent">添加一条数据</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      studentList: [
        { id: 1, name: '张三', age: 18 },
        { id: 2, name: '李四', age: 19 }
      ]
    };
  },
  methods: {
    addStudent(){
      const studentObj = { id: 3, name: '王五', age: 20 };
      this.studentList = [studentObj, ...this.studentList]
    }
  }
}
</script>

Even though only one item is added, the console shows that the whole list is re‑rendered.

Data misalignment

If the list contains input elements, using the index can cause the input values to shift after an insertion because the DOM nodes are reused incorrectly.

<template>
  <div class="hello">
    <ul>
      <li v-for="(item,index) in studentList" :key="index">
        {{item.name}}<input />
      </li>
    </ul>
    <button @click="addStudent">添加一条数据</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      studentList: [
        { id: 1, name: '张三', age: 18 },
        { id: 2, name: '李四', age: 19 }
      ]
    };
  },
  methods: {
    addStudent(){
      const studentObj = { id: 3, name: '王五', age: 20 };
      this.studentList = [studentObj, ...this.studentList]
    }
  }
}
</script>

After adding a new student, the input values of existing rows appear in the wrong rows.

Solutions

To avoid the pitfalls of using an index as a key, ensure that each item has a stable, unique identifier.

Use a unique field from the backend such as an ID, phone number, or ID card number.

Use Symbol values, which are guaranteed to be unique. let a = Symbol('测试') let b = Symbol('测试') console.log(a === b) // false

Generate a UUID for each item. import uuidv1 from 'uuid/v1' const item = { id: uuidv1() }

When a stable key is used, Vue’s diff algorithm can correctly reuse nodes, resulting in fewer DOM updates and no input misalignment.

Summary

Using an index as key causes unnecessary DOM updates during operations that change order, leading to performance loss.

If the list contains input elements, an index key can cause data to appear in the wrong rows.

Prefer stable, unique identifiers (ID, Symbol, UUID) as keys.

In scenarios where the list is purely static and never reordered, an index key may work, but using a proper key is a better habit.

Reference links:

Vue 3.0 diff algorithm detailed analysis (https://blog.csdn.net/zl_Alien/article/details/106595459)

Vue 3 Virtual DOM Diff source reading (https://segmentfault.com/a/1190000038654183)

FrontendperformanceVuediff algorithmlist renderingkey
政采云技术
Written by

政采云技术

ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.