diff --git a/src/core/vdom/patch.js b/src/core/vdom/patch.js index 368eb41afc..b5296db3c9 100644 --- a/src/core/vdom/patch.js +++ b/src/core/vdom/patch.js @@ -263,6 +263,9 @@ export function createPatchFunction (backend) { function createChildren (vnode, children, insertedVnodeQueue) { if (Array.isArray(children)) { + if (process.env.NODE_ENV !== 'production') { + checkDuplicateKeys(children) + } for (let i = 0; i < children.length; ++i) { createElm(children[i], insertedVnodeQueue, vnode.elm, null, true) } @@ -394,6 +397,10 @@ export function createPatchFunction (backend) { // during leaving transitions const canMove = !removeOnly + if (process.env.NODE_ENV !== 'production') { + checkDuplicateKeys(newCh) + } + while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { if (isUndef(oldStartVnode)) { oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left @@ -426,13 +433,6 @@ export function createPatchFunction (backend) { createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) } else { vnodeToMove = oldCh[idxInOld] - /* istanbul ignore if */ - if (process.env.NODE_ENV !== 'production' && !vnodeToMove) { - warn( - 'It seems there are duplicate keys that is causing an update error. ' + - 'Make sure each v-for item has a unique key.' - ) - } if (sameVnode(vnodeToMove, newStartVnode)) { patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue) oldCh[idxInOld] = undefined @@ -453,6 +453,24 @@ export function createPatchFunction (backend) { } } + function checkDuplicateKeys (children) { + const seenKeys = {} + for (let i = 0; i < children.length; i++) { + const vnode = children[i] + const key = vnode.key + if (isDef(key)) { + if (seenKeys[key]) { + warn( + `Duplicate keys detected: '${key}'. This may cause an update error.`, + vnode.context + ) + } else { + seenKeys[key] = true + } + } + } + } + function findIdxInOld (node, oldCh, start, end) { for (let i = start; i < end; i++) { const c = oldCh[i] diff --git a/test/unit/features/component/component.spec.js b/test/unit/features/component/component.spec.js index 52744c68ba..486a65033b 100644 --- a/test/unit/features/component/component.spec.js +++ b/test/unit/features/component/component.spec.js @@ -166,7 +166,7 @@ describe('Component', () => { const vm = new Vue({ template: '
' + - '' + + '' + '
', data: { comps: [{ type: 'one' }, { type: 'two' }] diff --git a/test/unit/modules/vdom/patch/children.spec.js b/test/unit/modules/vdom/patch/children.spec.js index 1bba91f11d..ec4d521955 100644 --- a/test/unit/modules/vdom/patch/children.spec.js +++ b/test/unit/modules/vdom/patch/children.spec.js @@ -507,4 +507,27 @@ describe('vdom patch: children', () => { expect(postPatch).toBe(original) }) + + it('should warn with duplicate keys: createChildren', () => { + function makeNode (key) { + return new VNode('div', { key: key }) + } + + const vnode = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode)) + patch(null, vnode) + expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned() + }) + + it('should warn with duplicate keys: updateChildren', () => { + function makeNode (key) { + return new VNode('div', { key: key }) + } + + const vnode2 = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode)) + const vnode3 = new VNode('p', {}, ['b', 'x', 'd', 'b'].map(makeNode)) + patch(vnode0, vnode2) + expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned() + patch(vnode2, vnode3) + expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned() + }) })