Frontend Development 12 min read

Why You Should Not Use Index or Random Numbers as Vue.js List Keys – Understanding the Diff Algorithm

This article explains the purpose of the key attribute in Vue, demonstrates why using the array index or random numbers as keys breaks the virtual‑DOM diff algorithm, and provides best‑practice recommendations for stable, unique keys to ensure efficient component updates.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Why You Should Not Use Index or Random Numbers as Vue.js List Keys – Understanding the Diff Algorithm

Preface

What is the purpose of key in Vue and why is using the index as a key discouraged? This article explores the underlying principles.

Example

Consider the following simple list:

<ul>
  <li>1</li>
  <li>2</li>
</ul>

The corresponding virtual DOM node ( vnode ) looks like this:

{
  tag: 'ul',
  children: [
    { tag: 'li', children: [{ vnode: { text: '1' } }] },
    { tag: 'li', children: [{ vnode: { text: '2' } }] }
  ]
}

After an update we swap the order of the child nodes:

{
  tag: 'ul',
  children: [
    +   { tag: 'li', children: [{ vnode: { text: '2' } }] },
    +   { tag: 'li', children: [{ vnode: { text: '1' } }] }
  ]
}

The children part is the focus of the diff algorithm discussed below.

When reactive data changes, the render watcher triggers vm._update(vm._render()) , which generates a new vnode . The vm._update function then calls __patch__ to apply the changes.

The patch process first compares the old and new nodes to see if they are of the same type.

1. Not the Same Node

If isSameNode returns false, the old vnode is destroyed and a new one is rendered, which explains why diff works on a per‑level basis.

2. Same Node – Reuse

If the nodes are the same (e.g., both ul ), Vue calls patchVNode from src/core/vdom/patch.js .

If the new vnode is a text vnode

Vue directly uses the browser DOM API to replace the text content.

If the new vnode is not a text vnode

If there are new children but no old children

Vue calls addVnodes to insert the new child nodes.

If there are old children but no new children

Vue calls removeVnodes to delete the old child nodes.

If both old and new children exist

This is the core of the diff algorithm: comparing the children arrays.

// old start node
let oldStartIdx = 0
// new start node
let newStartIdx = 0
// old end node
let oldEndIdx = oldCh.length - 1
// new end node
let newEndIdx = newCh.length - 1

These indices point to the start and end of the old and new child lists. A while loop repeatedly compares the nodes at both ends until no more comparisons are possible.

The helper sameVnode determines whether two nodes can be reused:

function sameVnode (a, b) {
  return (
    a.key === b.key && (
      (
        a.tag === b.tag &&
        a.isComment === b.isComment &&
        isDef(a.data) === isDef(b.data) &&
        sameInputType(a, b)
      )
    )
  )
}

The key is crucial for this check.

When the keys do not match, Vue falls back to creating new DOM elements:

// Build a key‑>index map for old children
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
// Find a reusable index in the old list
idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
if (isUndef(idxInOld)) {
  // No match – create a brand‑new element
  createElm()
}

Why Not Use Index as Key?

Consider a component that renders a list of numbers. Using the index as the key leads to the following old and new virtual children (both keep keys 0, 1, 2):

[{ tag: "item", key: 0, props: { num: 1 } },
 { tag: "item", key: 1, props: { num: 2 } },
 { tag: "item", key: 2, props: { num: 3 } }]

After reversing the array, the keys stay the same while the values change, causing Vue to patch the wrong vnode and trigger expensive updates (prop changes, attribute updates, class updates, listeners, etc.).

Why Not Use Random Numbers as Key?

Using Math.random() generates a new key for every render:

[{ tag: "item", key: 0.6330..., props: { num: 1 } },
 { tag: "item", key: 0.2510..., props: { num: 2 } },
 { tag: "item", key: 0.4114..., props: { num: 3 } }]

After an update the keys become completely different random values, so the diff algorithm cannot find any reusable nodes and creates three new components, then destroys the old ones – a performance disaster.

Summary

Through this walkthrough we learned:

Use a stable, unique identifier (usually provided by the backend) as the key . If none exists, generate one once when the list is fetched and keep it unchanged for the component’s lifetime.

Never use the array index as a key ; it defeats Vue’s ability to correctly reuse nodes when the order changes.

Avoid random numbers for key because they force Vue to recreate and destroy nodes on every update, causing severe performance penalties.

Vue.jsdiff algorithmfrontend performancelist renderingkey
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

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.