Heim > Web-Frontend > View.js > Hauptteil

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

青灯夜游
Freigeben: 2022-05-19 21:09:45
nach vorne
4223 Leute haben es durchsucht

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>
Nach dem Login kopieren
  • +new Date()生成的时间戳作为key,手动强制触发重新渲染

<Comp :key="+new Date()" />
Nach dem Login kopieren

那么这背后的逻辑是什么,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>
Nach dem Login kopieren

在不使用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)
            )
        )
    )
}
Nach dem Login kopieren
Nach dem Login kopieren

🎜🎜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]
        }
    }
    ...
}
Nach dem Login kopieren
Nach dem Login kopieren
🎜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>vue</code >Dieser Vorgang wird ausgeführt: 🎜🎜<img src="https://img.php.cn/upload/image/261/425/754/1652965627545335.png " title="1652965627545335.png" alt="1. png"/>🎜🎜Analysieren Sie den Gesamtprozess: 🎜<ul>🎜Vergleichen Sie A, A, Knoten desselben Typs, führen Sie <code>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</li>🎜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-for</code zu verwenden > wann immer möglich Geben Sie <code>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)
            )
        )
    )
}
Nach dem Login kopieren
Nach dem Login kopieren

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]
        }
    }
    ...
}
Nach dem Login kopieren
Nach dem Login kopieren

(学习视频分享: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!

Verwandte Etiketten:
Quelle:juejin.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage