Skip to content

Commit 5015a00

Browse files
gebilaoxiongaJean
authored andcommitted
fix(core): warn duplicate keys in all cases (vuejs#7200)
close vuejs#7199
1 parent 4f432be commit 5015a00

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

src/core/vdom/patch.js

+25-7
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ export function createPatchFunction (backend) {
263263

264264
function createChildren (vnode, children, insertedVnodeQueue) {
265265
if (Array.isArray(children)) {
266+
if (process.env.NODE_ENV !== 'production') {
267+
checkDuplicateKeys(children)
268+
}
266269
for (let i = 0; i < children.length; ++i) {
267270
createElm(children[i], insertedVnodeQueue, vnode.elm, null, true)
268271
}
@@ -394,6 +397,10 @@ export function createPatchFunction (backend) {
394397
// during leaving transitions
395398
const canMove = !removeOnly
396399

400+
if (process.env.NODE_ENV !== 'production') {
401+
checkDuplicateKeys(newCh)
402+
}
403+
397404
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
398405
if (isUndef(oldStartVnode)) {
399406
oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left
@@ -426,13 +433,6 @@ export function createPatchFunction (backend) {
426433
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm)
427434
} else {
428435
vnodeToMove = oldCh[idxInOld]
429-
/* istanbul ignore if */
430-
if (process.env.NODE_ENV !== 'production' && !vnodeToMove) {
431-
warn(
432-
'It seems there are duplicate keys that is causing an update error. ' +
433-
'Make sure each v-for item has a unique key.'
434-
)
435-
}
436436
if (sameVnode(vnodeToMove, newStartVnode)) {
437437
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
438438
oldCh[idxInOld] = undefined
@@ -453,6 +453,24 @@ export function createPatchFunction (backend) {
453453
}
454454
}
455455

456+
function checkDuplicateKeys (children) {
457+
const seenKeys = {}
458+
for (let i = 0; i < children.length; i++) {
459+
const vnode = children[i]
460+
const key = vnode.key
461+
if (isDef(key)) {
462+
if (seenKeys[key]) {
463+
warn(
464+
`Duplicate keys detected: '${key}'. This may cause an update error.`,
465+
vnode.context
466+
)
467+
} else {
468+
seenKeys[key] = true
469+
}
470+
}
471+
}
472+
}
473+
456474
function findIdxInOld (node, oldCh, start, end) {
457475
for (let i = start; i < end; i++) {
458476
const c = oldCh[i]

test/unit/features/component/component.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ describe('Component', () => {
166166
const vm = new Vue({
167167
template:
168168
'<div>' +
169-
'<component v-for="c in comps" :key="c.type" :is="c.type"></component>' +
169+
'<component v-for="(c, i) in comps" :key="i" :is="c.type"></component>' +
170170
'</div>',
171171
data: {
172172
comps: [{ type: 'one' }, { type: 'two' }]

test/unit/modules/vdom/patch/children.spec.js

+23
Original file line numberDiff line numberDiff line change
@@ -507,4 +507,27 @@ describe('vdom patch: children', () => {
507507

508508
expect(postPatch).toBe(original)
509509
})
510+
511+
it('should warn with duplicate keys: createChildren', () => {
512+
function makeNode (key) {
513+
return new VNode('div', { key: key })
514+
}
515+
516+
const vnode = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode))
517+
patch(null, vnode)
518+
expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()
519+
})
520+
521+
it('should warn with duplicate keys: updateChildren', () => {
522+
function makeNode (key) {
523+
return new VNode('div', { key: key })
524+
}
525+
526+
const vnode2 = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode))
527+
const vnode3 = new VNode('p', {}, ['b', 'x', 'd', 'b'].map(makeNode))
528+
patch(vnode0, vnode2)
529+
expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()
530+
patch(vnode2, vnode3)
531+
expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned()
532+
})
510533
})

0 commit comments

Comments
 (0)