Do you know the principle of key in vue? Tell me your understanding of it

Interviewer: Do you know the principle of key in Vue? Tell me your understanding of it

 

1. What is Key?

Before we begin, let’s restore two actual work scenarios

  1. When we use v-for, we need to add a key to the unit
xml复制代码<ul>
    <li v-for="item in items" :key="item.id">...</li>
</ul>
  1. Use the timestamp generated by +new Date() as the key to manually force re-rendering.
  2. iniCopy code
  3. <Comp :key="+new Date()" />

So what is the logic behind this and what is the role of key?

In one sentence

Key is the unique ID given to each vnode. It is also an optimization strategy for diff. According to the key, the corresponding vnode node can be found more accurately and faster.

The logic behind the scene

When we use v-for, we need to add a key to the unit

  • If keys are not used, Vue will adopt the in-place principle: minimize the movement of elements, and try to patch or reuse elements of the same type in the same appropriate place to the greatest extent possible.
  • If a key is used, Vue will record the elements according to the order of the keys. If the element that once had the key no longer appears, it will be directly removed or destroyed.

Use the timestamp generated by +new Date() as the key to manually force re-rendering.

  • When a rerender with a new value is used as a key and a Comp with a new key appears, the old key Comp will be removed and the new key Comp will trigger rendering.

2. The difference between setting key and not setting key

for example:

Create an instance and insert data into the items array after 2 seconds

xml复制代码<body>
  <div id="demo">
    <p v-for="item in items" :key="item">{
   
   {item}}</p>
  </div>
  <script src="../../dist/vue.js"></script>
  <script>
    // 创建实例
    const app = new Vue({
      el: '#demo',
      data: { items: ['a', 'b', 'c', 'd', 'e'] },
      mounted () {
        setTimeout(() => { 
          this.items.splice(2, 0, 'f')  // 
       }, 2000);
     },
   });
  </script>
</body>

Without using keys, vue will perform the following operations:

 

Analyze the overall process:

  • Compare A, A, nodes of the same type, perform patching, but the data is the same, no DOM operation occurs
  • Compare B, B, nodes of the same type, perform patching, but the data is the same, no DOM operation occurs
  • Compare C, F, nodes of the same type, perform patching, the data is different, and DOM operations occur.
  • Compare D and C, nodes of the same type, perform patching, the data is different, and DOM operations occur.
  • Compare E, D, nodes of the same type, perform patching, if the data is different, DOM operation occurs.
  • The loop ends and E is inserted into the DOM.

A total of 3 updates and 1 insert operation occurred

When using key: vue will perform the following operations:

  • Compare A, A, nodes of the same type, perform patching, but the data is the same, no DOM operation occurs
  • Compare B, B, nodes of the same type, perform patching, but the data is the same, no DOM operation occurs
  • Compare C, F, nodes of different types
    • Compare E and E, nodes of the same type, and perform patching, but the data is the same and no DOM operation occurs.
  • Compare D and D, nodes of the same type, and perform patching, but the data is the same and no DOM operation occurs.
  • Compare C and C, nodes of the same type, and perform patching, but the data is the same and no DOM operation occurs.
  • At the end of the loop, insert F before C

A total of 0 updates and 1 insert operation occurred

Through the above two small examples, it can be seen that setting the key can greatly reduce the DOM operations on the page and improve the diff efficiency.

Will setting the key value definitely improve diff efficiency?

In fact, this is not the case, and it is clearly stated in the document

When Vue.js is updating a list of rendered elements using v-for, it defaults to the "in-place reuse" strategy. If the order of the data items is changed, Vue will not move the DOM elements to match the order of the data items, but will simply reuse each element here and make sure it displays each element that has been rendered at a specific index.

This default mode is efficient, but only suitable for list rendering output that does not rely on child component state or temporary DOM state (for example: form input values)

It is recommended to provide a key when using v-for whenever possible, unless traversing the output DOM content is very simple, or you deliberately rely on the default behavior to obtain performance improvements.

3. Principle analysis

Source code location: core/vdom/patch.js

Here to determine whether it is the same key, the first thing to determine is whether the key values ​​​​are equal. If the key is not set, then the key is undefined. At this time, undefined is always equal to undefined.

css复制代码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)
            ) || (
                isTrue(a.isAsyncPlaceholder) &&
                a.asyncFactory === b.asyncFactory &&
                isUndef(b.asyncFactory.error)
            )
        )
    )
}

In the updateChildren method, the old and new vnodes will be diffed, and then the comparison results will be used to update the real DOM.

scss复制代码function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
    ...
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        if (isUndef(oldStartVnode)) {
            ...
        } else if (isUndef(oldEndVnode)) {
            ...
        } else if (sameVnode(oldStartVnode, newStartVnode)) {
            ...
        } else if (sameVnode(oldEndVnode, newEndVnode)) {
            ...
        } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
            ...
        } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
            ...
        } else {
            if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
            idxInOld = isDef(newStartVnode.key)
                ? oldKeyToIdx[newStartVnode.key]
                : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)
            if (isUndef(idxInOld)) { // New element
                createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
            } else {
                vnodeToMove = oldCh[idxInOld]
                if (sameVnode(vnodeToMove, newStartVnode)) {
                    patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
                    oldCh[idxInOld] = undefined
                    canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
                } else {
                    // same key but different element. treat as new element
                    createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)
                }
            }
            newStartVnode = newCh[++newStartIdx]
        }
    }
    ...
}

Guess you like

Origin blog.csdn.net/Cipher_Y/article/details/132103543