Explain the key value in Vue in simple terms

From the previous article

I wrote an article the other day, sortable.js -- Vue data update problem At that time, I only analyzed the data from the perspective of forced refresh, and I didn't find the real "culprit".

Thank someone for pointing out that the key value of Vue may lead to incorrect data rendering. Therefore, I made a further attempt.

An incorrect use of key -- using index as key

I wonder if you will directly use index as its key value when writing v-for. Yes, I admit I will. I have to say that this is really not a good habit.

According to the previous article, we still use sortable.js as an example. The following is the core code, where the value of arrData is [1,2,3,4]

<div id="sort">
  <div v-for="(item,index) in arrData" :key="index" >
Copy code
  mounted () {
    let el = document.getElementById('sort')
    var sortable = new Sortable(el, {
      onEnd: (e) => {
        const tempItem = this.arrData.splice(e.oldIndex, 1)[0]
        this.arrData.splice(e.newIndex, 0, tempItem)
Copy code

Of course, at the beginning, there must be no problem with data rendering


OK, let's look at the following operations:


You can see that when I drag 3 to 2, the following data becomes 1342, but the view above is 1234. Then when I drag the fourth position to the third position, the following data also takes effect, but the above data seems to be all disordered. Well, we recreated the crime scene.

Then I changed the key value of the binding. Because the example here is special, we think that the value of item is different

<div id="sort">
  <div v-for="(item,index) in arrData" :key="item" >
Copy code

Look at the effect again:


Yes, at this time, the data is completely synchronized with the view.


Let's first look at an introduction to key in the official document

Child elements with the same parent element must have a unique key. Duplicate keys can cause rendering errors.

The reason for the above rendering error is that our key value is not unique. For example, after adjusting the array order, the original key value of each item changes, resulting in rendering error.

Let's draw a conclusion first. Using index as the key value is hidden unless you can guarantee that index can always be used as a unique identifier

What's the use of key value

After vue 2.0, if we don't write the key, we will report warning. That is to say, the official wants us to write the key value. What role does the key play in vue?

Can performance be improved without using key? The answer is yes! sure!

First look at the official explanation:

Without keys, Vue uses an algorithm that minimizes dynamic elements and tries to repair / reuse elements of the same type as much as possible. Using key, it rearranges the order of elements based on the change of key, and removes elements that do not exist.

For example, if an array [1,2,3,4] becomes [2,1,3,4], a "local update strategy" will be adopted if there is no key value, as shown in the figure below. It does not move the position of the element node, but directly modifies the element itself, which saves some performance


For elements with key values, the update method is shown in the following figure. As you can see, it is a removal / addition operation for DOM, which is more performance-consuming.


The performance is better without a key. Why do you need to bring a key? Let's take a look at an example. The core code is as follows. Here we imitate the function of a switching tab, that is, the switching tab1 is 1,2,3,4. tab2 is 5,6,7,8. There is a function of clicking to set the first font color to red.

Then, when we click tab1 to set the font color to red, and then switch to tab2, we expect the result to be the initial color of our first font instead of red, but the result is still red.

<div id="sort">
  <button @click="trunToTab1">tab1</button>
  <button @click="trunToTab2">tab2</button>
  <div v-for="(item, index) in arrData">
    <div @click="clickItem(index)" class="item">{{item}}</div>
Copy code
      trunToTab1 () {
        this.arrData = [1,2,3,4]
      trunToTab2 () {
        this.arrData = [5,6,7,8]
      clickItem () {
        document.getElementsByClassName('item')[0].style.color = 'red'
Copy code


This is beyond our expectation, that is, the official document says that the default mode refers to the state without key. This mode is not applicable to those that depend on the sub component state or temporary DOM state.

This default mode is efficient, but only applies to list rendered output that does not depend on subcomponent state or temporary DOM state (for example, form input value).

Let's look at the effect with the key


This is why the official document recommends us to write a key. According to the introduction of the document, it is as follows:

Using key, it rearranges the order of elements based on the change of key, and removes elements that do not exist. It can also be used to force replacement of elements / components rather than reuse it. It may be useful when you encounter the following scenarios:

  • Trigger the lifecycle hook of the component completely
  • Trigger transition

So how does Vue's underlying key value achieve the above functions? We have to talk about diff algorithm and virtual DOM.

The role of key in diff algorithm

Here, we will not talk about the specific of diff algorithm, but only look at the role of key value in it. (we'll talk about diff algorithm when we have a chance)

Look at src/core/vdom/patch.js in vue source code

if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
        idxInOld = isDef(newStartVnode.key)
          ? oldKeyToIdx[newStartVnode.key]
          : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
Copy code

Let's sort out the code blocks:

  // If there is a key
  if (isUndef(oldKeyToIdx)) {
    // Create index table
    oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
  if (isDef(newStartVnode.key)) {
    // If you have a key, you can get it directly from the above creation
    idxInOld = oldKeyToIdx[newStartVnode.key]
  } else {
    // If there is no key, call findIdxInOld
    idxInOld = findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
Copy code

The most important thing is the comparison between the createKeyToOldIdx and findIdxInOld functions. What did they do?

function createKeyToOldIdx (children, beginIdx, endIdx) {
  let i, key
  const map = {}
  for (i = beginIdx; i <= endIdx; ++i) {
    key = children[i].key
    if (isDef(key)) map[key] = i
  return map
Copy code
 function findIdxInOld (node, oldCh, start, end) {
    for (let i = start; i < end; i++) {
      const c = oldCh[i]
      if (isDef(c) && sameVnode(node, c)) return i
Copy code

We can see that if we have a key value, we can directly find the corresponding value in the map object created in the createKeyToOldIdx method according to our key value. If there is no key value, you need to traverse to get it. Mapping is faster than traversal.

The key value is the unique identifier of each vnode. Relying on the key, we can get the corresponding node in oldVnode faster.

reference resources

Question 1: why should I write a key in the list component when writing a React / Vue project? What is its function?

diff algorithm for parsing vue2.0

Welcome to my front-end grocery store~

Author: Gopal
Link: https://juejin.cn/post/6844903865930743815
Source: rare earth Nuggets
The copyright belongs to the author. For commercial reprint, please contact the author for authorization, and for non-commercial reprint, please indicate the source.

Tags: Vue Vue.js

Posted on Wed, 10 Nov 2021 07:14:43 -0500 by Kaz