Qu'est-ce que Key in vue ? L'article suivant vous présentera le principe de la clé dans vue et parlera de la différence entre définir la clé et ne pas définir la clé. J'espère que cela sera utile à tout le monde !
Avant de commencer, restaurons deux scénarios de travail réels
Lorsque nous utilisons v-for
, nous devons l'ajouter à l'unité key
v-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
用+new Date()
生成的时间戳作为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: '#demo', data: { items: ['a', 'b', 'c', 'd', 'e'] }, mounted () { setTimeout(() => { this.items.splice(2, 0, 'f') // }, 2000); }, }); </script> </body>
在不使用key
的情况,vue
会进行这样的操作:
分析下整体流程:
patch
,但数据相同,不发生dom
操作patch
,但数据相同,不发生dom
操作patch
,数据不同,发生dom
操作patch
,数据不同,发生dom
操作patch
,数据不同,发生dom
操作DOM
中一共发生了3次更新,1次插入操作
在使用key
的情况:vue
会进行这样的操作:
patch
,但数据相同,不发生dom
操作patch
,但数据相同,不发生dom
操作patch
,但数据相同,不发生dom
操作patch
,但数据相同,不发生dom
操作patch
,但数据相同,不发生dom
操作一共发生了0次更新,1次插入操作
通过上面两个小例子,可见设置key
能够大大减少对页面的DOM
操作,提高了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) ) ) ) }
+new Date()
comme key
et forcez-le Déclenchez manuellement le nouveau rendu🎜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] } } ... }
clé
? 🎜🎜En une phrase🎜🎜🎜key est l'ID unique donné à chaque vnode, et c'est aussi une stratégie d'optimisation de diff. Selon la clé, le nœud vnode correspondant peut être trouvé plus précisément et plus rapidement. 🎜 (Partage de vidéos d'apprentissage : vue vidéo tutoriel🎜)🎜v-for
, nous devons ajouter une clé
au unit 🎜 L'horodatage généré par +new Date()
est utilisé comme clé
pour déclencher manuellement le nouveau rendu🎜items
après 2 secondes🎜rrreee🎜Lorsque key</ code> n'est pas utilisé, <code>vue</code >Cette opération sera effectuée : 🎜🎜<img src="https://img.php.cn/upload/image/261/425/754/1652965627545335.png " title="1652965627545335.png" alt="1. png"/>🎜🎜Analyser le processus global : 🎜<ul>🎜Comparez A, A, les nœuds du même type, effectuez un <code>patch
, mais les données sont les mêmes, dom
ne se produit pas. Opération🎜Comparez B, B, nœuds du même type, effectuez patch
, mais les données sont les pareil, aucune opération dom
ne se produira🎜Comparez C, F, les nœuds du même type, effectuez un patch
, les données sont différentes, dom</ code> l'opération se produit</li>🎜Comparez D, C, les nœuds du même type, effectuez <code>patch< /code>, les données sont différentes, l'opération <code>dom
se produit🎜 Comparez E, D, nœuds du même type, effectuez patch
, les données sont différentes, dom se produit
Opération🎜La boucle se termine, insérez E dans DOM
🎜Un total de 3 mises à jour et 1 opération d'insertion ont eu lieu🎜🎜En utilisation Dans le cas de key
: vue
effectuera cette opération : 🎜patch
, mais les données sont les mêmes, l'opération dom
ne le fait pas se produisent🎜Comparez B, B, les nœuds du même type, effectuez un patch
, mais les données sont les mêmes, dom ne se produit pas
Opération 🎜Comparez C, F, nœuds de types différents patch
, mais les données sont les mêmes, cela n'arrive pas dom Opération
patch
, mais les données sont les mêmes, dom
🎜Comparez C et C, nœuds du même type, effectuez un patch
, mais les données sont les mêmes, pas de dom
l'opération se produira🎜La boucle se termine et F est inséré avant Cclé
est définie. Cela peut réduire considérablement les opérations DOM
sur la page et améliorer l'efficacité diff
🎜v-for</code > dans la mesure du possible, fournissez une <code>clé
lorsque vous parcourez le contenu DOM de sortie est très simple, ou vous vous appuyez délibérément sur le comportement par défaut pour obtenir des améliorations de performances🎜源码位置:core/vdom/patch.js
里判断是否为同一个key
,首先判断的是key
值是否相等如果没有设置key
,那么key
为undefined
,这时候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] } } ... }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!