最近遇到的一个问题
平时在使用重复key的时候,会经常遇到报错
Duplicate keys detected: 'xxxxxx'. This may cause an update error. found in xxxxx
以前项目中在后端数据有重复数据的时候经常会遇到这个warning,这个一般只是一个警告,不会导致运行时报错。但最近在项目中由于Duplicate keys 导致update时报错,看了一下源码,研究出了其中的原因。
这是一段会报错的代码
<template>
<div>
<el-checkbox v-model="checkAll" @change="handleCheckAllChange">改变第一个和最后一个l对象的key和value</el-checkbox>
<ul>
<li v-for="city in childList" :key="city.code">{{city.label}}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
childList :[
{label:"原来的标签1",code:"label1",value:true},
{label:"原来的标签2",code:"label2",value:true},
{label:"原来的标签3",code:"label3",value:true},
{label:"原来的标签4",code:"label3",value:true},
{label:"原来的标签5",code:"label4",value:true},
{label:"原来的标签6",code:"label6",value:true},
]
};
},
methods: {
changeChildList(){
this.childList=[
{label:"修改的标签1",code:"labelupdate1",value:true},
{label:"原来的标签2",code:"label2",value:true},
{label:"原来的标签3",code:"label3",value:true},
{label:"原来的标签4",code:"label3",value:true},
{label:"原来的标签5",code:"label4",value:true},
{label:"修改的标签6",code:"labelupdate6",value:true},
]
},
handleCheckAllChange(val) {
this.changeChildList()
}
}
};
</script>
**报错的原因如下, 首先,报错的地方是在function sameVnode内
function sameVnode (a, b) {
return (
a.key === b.key && ( //这里报了错,a是undefined,也就是说第一个参数找不到了
(
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)
)
)
)
}
注释一下报错的原因
function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {
//其他代码先忽略,因为本例子中是list的第一个元素和最后元素改变了,且不是第一次渲染
//所以前面的6种比较情况分别是,
// 1.oldstart vs newstart,2.oldstart vs newend 3.oldend vs newstart,2.oldend vs newend 5.oldStartVnode isundefined 6。oldEndVnode is undefined
//上面的均不执行,直接执行else,也就是下面所展示的代码
//首先我们这个例子是定义了key
//如果有定义key的时候,则直接对比key,在oldKeyToIdx找到vnode
//如果key相同(即使其他属性不一样),则认为在oldKeyToIdx是同一个vnode
//所以当重复key的话,idxInOld还是返回和之前的key同一个的idxInOld
//但是用oldCh[idxInOld]去到的vnode,因为在前一个相同key的vnode已经取到了oldCh[idxInOld]并将oldCh[idxInOld];设为unde
//所以第二个重复key取值的时候,能找到idxInOld
//但是vnodeToMove =oldCh[idxInOld],vnodeToMove就只能取到undefined了
//也就导致了 if (sameVnode(vnodeToMove, newStartVnode)){}
//里的第一个参数为undefined,报错就抛出了Cannot read property 'key' of undefined
idxInOld = isDef(newStartVnode.key)
? oldKeyToIdx[newStartVnode.key]//本例子有定义key
: findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
if (isUndef(idxInOld)) {
//如果在oldKeyToIdx或oldKeyToIdx都找不到这idx,说明是新增vnode,直接创建
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);
} else {
//否则说明这个vnode在oldCh里面存在,直接复用这个vnode
vnodeToMove = oldCh[idxInOld];//这里是undefine,所以导致下面的sameVnode的第一个参数是undefined,导致报错
if (sameVnode(vnodeToMove, newStartVnode)) {//这里
···
//找到这个vnode复用后把它从oldCh里面删掉
oldCh[idxInOld] = undefined;
}
}
可能下面这张图也许大概应该会相对清楚一点
这篇文章主要作为我学习vue源码的一个记录,如果有不妥当的地方,请各位帮忙指出,多多指教,谢谢你们。