Heim  >  Artikel  >  Web-Frontend  >  Was ist Key in Vue? Was ist der Unterschied zwischen dem Setzen des Schlüssels und dem Nicht-Setzen?

Was ist Key in Vue? Was ist der Unterschied zwischen dem Setzen des Schlüssels und dem Nicht-Setzen?

青灯夜游
青灯夜游nach vorne
2022-05-19 21:09:454206Durchsuche

Was ist Key in Vue? Der folgende Artikel stellt Ihnen das Prinzip der Schlüsseleingabe vue vor und erläutert den Unterschied zwischen dem Festlegen des Schlüssels und dem Nicht-Festlegen des Schlüssels. Ich hoffe, dass er für alle hilfreich ist!

Was ist Key in Vue? Was ist der Unterschied zwischen dem Setzen des Schlüssels und dem Nicht-Setzen?

1. Was ist der Schlüssel

Bevor wir beginnen, stellen wir zwei tatsächliche Arbeitsszenarien wieder her

  • Wenn wir v-for verwenden, müssen wir es der Einheit hinzufügen keyv-for时,需要给单元加上key

<ul>
    <li v-for="item in items" :key="item.id">...</li>
</ul>
  • +new Date()生成的时间戳作为key,手动强制触发重新渲染

<Comp :key="+new Date()" />

那么这背后的逻辑是什么,key的作用又是什么?

一句话来讲

key是给每一个vnode的唯一id,也是diff的一种优化策略,可以根据key,更准确, 更快的找到对应的vnode节点。(学习视频分享:vue视频教程

场景背后的逻辑

当我们在使用v-for时,需要给单元加上key

  • 如果不用key,Vue会采用就地复地原则:最小化element的移动,并且会尝试尽最大程度在同适当的地方对相同类型的element,做patch或者reuse。
  • 如果使用了key,Vue会根据keys的顺序记录element,曾经拥有了key的element如果不再出现的话,会被直接remove或者destoryed

+new Date()生成的时间戳作为key,手动强制触发重新渲染

  • 当拥有新值的rerender作为key时,拥有了新key的Comp出现了,那么旧key Comp会被移除,新key Comp触发渲染

二、设置key与不设置key区别

举个例子:
创建一个实例,2秒后往items数组插入数据

<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: &#39;#demo&#39;,
      data: { items: [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;, &#39;e&#39;] },
      mounted () {
        setTimeout(() => { 
          this.items.splice(2, 0, &#39;f&#39;)  // 
       }, 2000);
     },
   });
  </script>
</body>

在不使用key的情况,vue会进行这样的操作:

Was ist Key in Vue? Was ist der Unterschied zwischen dem Setzen des Schlüssels und dem Nicht-Setzen?

分析下整体流程:

  • 比较A,A,相同类型的节点,进行patch,但数据相同,不发生dom操作
  • 比较B,B,相同类型的节点,进行patch,但数据相同,不发生dom操作
  • 比较C,F,相同类型的节点,进行patch,数据不同,发生dom操作
  • 比较D,C,相同类型的节点,进行patch,数据不同,发生dom操作
  • 比较E,D,相同类型的节点,进行patch,数据不同,发生dom操作
  • 循环结束,将E插入到DOM

一共发生了3次更新,1次插入操作

在使用key的情况:vue会进行这样的操作:

  • 比较A,A,相同类型的节点,进行patch,但数据相同,不发生dom操作
  • 比较B,B,相同类型的节点,进行patch,但数据相同,不发生dom操作
  • 比较C,F,不相同类型的节点
    • 比较E、E,相同类型的节点,进行patch,但数据相同,不发生dom操作
  • 比较D、D,相同类型的节点,进行patch,但数据相同,不发生dom操作
  • 比较C、C,相同类型的节点,进行patch,但数据相同,不发生dom操作
  • 循环结束,将F插入到C之前

一共发生了0次更新,1次插入操作

通过上面两个小例子,可见设置key能够大大减少对页面的DOM操作,提高了diff效率

设置key值一定能提高diff效率吗?

其实不然,文档中也明确表示 

当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素 

这个默认的模式是高效的,但是只适用于不依赖子组件状态或临时 DOM 状态 (例如:表单输入值) 的列表渲染输出 

建议尽可能在使用 v-for 时提供 key

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)
            )
        )
    )
}

🎜🎜Verwenden Sie den von +new Date() generierten Zeitstempel als key und erzwingen Sie ihn Manuell ein erneutes Rendern auslösen🎜
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]
        }
    }
    ...
}
🎜Was ist also die Logik dahinter und welche Rolle spielt der Schlüssel? 🎜🎜In einem Satz🎜🎜🎜Schlüssel ist die eindeutige ID, die jedem V-Knoten zugewiesen wird, und es ist auch eine Optimierungsstrategie von Diff. Gemäß dem Schlüssel kann der entsprechende V-Knoten genauer und schneller gefunden werden. 🎜 (Teilen von Lernvideos: vue Video Tutorial🎜)🎜

🎜Die Logik hinter der Szene🎜

🎜Wenn wir v-for verwenden, müssen wir key zum hinzufügen Einheit 🎜
    🎜Wenn keine Schlüssel verwendet werden, wendet Vue das In-Place-Prinzip an: Minimieren Sie die Bewegung von Elementen und versuchen Sie, Elemente desselben Typs so weit wie möglich an derselben geeigneten Stelle zu patchen oder wiederzuverwenden. 🎜Wenn ein Schlüssel verwendet wird, zeichnet Vue das Element entsprechend der Reihenfolge der Schlüssel auf. Wenn das Element, das einmal den Schlüssel hatte, nicht mehr erscheint, wird es direkt entfernt oder zerstört
🎜 Verwenden Sie . Der von +new Date() generierte Zeitstempel wird als Schlüssel verwendet, um das erneute Rendern manuell auszulösen🎜
    🎜Wenn das erneute Rendern mit dem neuen Wert als verwendet wird key, Comp mit dem neuen Schlüssel erscheint, dann wird der alte Schlüssel Comp entfernt und der neue Schlüssel Comp löst das Rendern aus

🎜2 Unterschied zwischen dem Festlegen des Schlüssels und dem Nicht-Festlegen des Schlüssels🎜🎜🎜Beispiel Beispiel:
Erstellen Sie eine Instanz und fügen Sie nach 2 Sekunden Daten in das Array items ein🎜rrreee🎜Wenn key code> wird nicht verwendet, <code>vueDieser Vorgang wird ausgeführt: 🎜🎜1. png🎜🎜Analysieren Sie den Gesamtprozess: 🎜
    🎜Vergleichen Sie A, A, Knoten desselben Typs, führen Sie patch durch, aber die Daten sind die gleichen, dom tritt nicht auf. Operation🎜Vergleichen Sie B, B, Knoten desselben Typs, führen Sie patch aus, aber die Daten sind die Dasselbe, es findet keine dom-Operation statt🎜Vergleichen Sie C, F, Knoten desselben Typs, führen Sie patch durch, Daten sind unterschiedlich, dom Code>-Vorgang tritt auf🎜Vergleichen Sie D, C, Knoten desselben Typs, führen Sie <code>Patch durch, die Daten sind unterschiedlich, dom-Vorgang tritt auf🎜 Vergleichen Sie E, D, Knoten desselben Typs, führen Sie patch aus, die Daten sind unterschiedlich, dom tritt auf Operation🎜Die Schleife endet, fügen Sie E in DOM
🎜Es wurden insgesamt 3 Aktualisierungen und 1 Einfügevorgang durchgeführt🎜🎜Verwendet Im Fall von key: vue führt diesen Vorgang aus: 🎜
    🎜Vergleichen Sie A, A, Knoten desselben Typs und führen Sie einen patch aus. Die Daten sind jedoch dieselben, die dom-Operation jedoch nicht auftreten🎜Vergleichen Sie B, B, Knoten desselben Typs, führen Sie einen Patch durch, aber die Daten sind dieselben, dom tritt nicht aufOperation 🎜Vergleichen Sie C, F, Knoten unterschiedlichen Typs
      🎜Vergleichen Sie E, E, Knoten desselben Typs, führen Sie patch durch, aber die Daten sind gleich, es passiert nicht dom -Operation
    🎜Vergleichen Sie D und D, Knoten desselben Typs, führen Sie einen Patch durch, aber die Daten sind dieselben, dom-Operation🎜Vergleichen Sie C und C, Knoten desselben Typs, führen Sie patch durch, aber die Daten sind dieselben, dom-Operation tritt nicht auf🎜Die Schleife endet und F wird vor C eingefügt
🎜Es sind insgesamt 0 Aktualisierungen und 1 Einfügevorgang aufgetreten🎜🎜Anhand der beiden oben genannten kleinen Beispiele ist dies ersichtlich Der Schlüssel ist festgelegt. Dies kann die DOM-Vorgänge auf der Seite erheblich reduzieren und die diff-Effizienz verbessern🎜

🎜Kann das Festlegen des Schlüsselwerts die Diff-Effizienz definitiv verbessern? ? 🎜

🎜Tatsächlich ist dies nicht der Fall. Das Dokument besagt auch eindeutig, dass 🎜🎜Wenn Vue.js v-for zum Aktualisieren der gerenderten Elementliste verwendet, standardmäßig die Strategie der „In-Place-Wiederverwendung“ verwendet. Wenn die Reihenfolge der Datenelemente geändert wird, verschiebt Vue die DOM-Elemente nicht, um sie an die Reihenfolge der Datenelemente anzupassen, sondern verwendet einfach jedes Element hier wieder und stellt sicher, dass jedes Element angezeigt wird, das an einem bestimmten Index gerendert wurde 🎜 🎜 Dieser Standardmodus ist effizient, eignet sich jedoch nur für Listenwiedergabeausgaben, die nicht auf dem Unterkomponentenstatus oder dem temporären DOM-Status basieren (z. B. Formulareingabewerte). 🎜🎜Es wird empfohlen, v-forzu verwenden > wann immer möglich Geben Sie key an, wenn das Durchlaufen des Ausgabe-DOM-Inhalts sehr einfach ist, oder Sie verlassen sich bewusst auf das Standardverhalten, um Leistungsverbesserungen zu erzielen🎜

三、原理分析

源码位置:core/vdom/patch.js 

里判断是否为同一个key,首先判断的是key值是否相等如果没有设置key,那么keyundefined,这时候undefined是恒等于undefined

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)
            )
        )
    )
}

updateChildren方法中会对新旧vnode进行diff,然后将比对出的结果用来更新真实的DOM

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]
        }
    }
    ...
}

(学习视频分享:web前端开发编程基础视频

Das obige ist der detaillierte Inhalt vonWas ist Key in Vue? Was ist der Unterschied zwischen dem Setzen des Schlüssels und dem Nicht-Setzen?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen