From c7620b21326758ef4b9231a24a6236f473bd73e0 Mon Sep 17 00:00:00 2001 From: gebilaoxiong Date: Sat, 9 Dec 2017 15:43:43 +0800 Subject: [PATCH 1/3] fix(patch): warn error when vnode with duplicate key --- src/core/vdom/patch.js | 31 ++++++++++++++----- .../unit/features/component/component.spec.js | 2 +- test/unit/modules/vdom/patch/children.spec.js | 23 ++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/core/vdom/patch.js b/src/core/vdom/patch.js index 368eb41afc0..7636a36270d 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') { + checkChildren(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') { + checkChildren(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,23 @@ export function createPatchFunction (backend) { } } + function checkChildren (children) { + const cache = {} + + for (let i = 0; i < children.length; i++) { + const vnode = children[i] + const key = vnode.key + + if (key != null) { + if (cache[key]) { + warn(`The duplicate keys: '${key}' that is causing an update error.`) + } else { + cache[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 52744c68bad..486a65033b0 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 1bba91f11d2..1f92eeb2f11 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('The duplicate keys: \'b\' that is causing an update error.').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('The duplicate keys: \'b\' that is causing an update error.').toHaveBeenWarned() + patch(vnode2, vnode3) + expect('The duplicate keys: \'b\' that is causing an update error.').toHaveBeenWarned() + }) }) From 1507408dc706197f92d379c20bc2327f0497a79e Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 12 Dec 2017 16:12:49 -0500 Subject: [PATCH 2/3] Update patch.js --- src/core/vdom/patch.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/core/vdom/patch.js b/src/core/vdom/patch.js index 7636a36270d..b5296db3c9d 100644 --- a/src/core/vdom/patch.js +++ b/src/core/vdom/patch.js @@ -264,7 +264,7 @@ export function createPatchFunction (backend) { function createChildren (vnode, children, insertedVnodeQueue) { if (Array.isArray(children)) { if (process.env.NODE_ENV !== 'production') { - checkChildren(children) + checkDuplicateKeys(children) } for (let i = 0; i < children.length; ++i) { createElm(children[i], insertedVnodeQueue, vnode.elm, null, true) @@ -398,7 +398,7 @@ export function createPatchFunction (backend) { const canMove = !removeOnly if (process.env.NODE_ENV !== 'production') { - checkChildren(newCh) + checkDuplicateKeys(newCh) } while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { @@ -453,18 +453,19 @@ export function createPatchFunction (backend) { } } - function checkChildren (children) { - const cache = {} - + function checkDuplicateKeys (children) { + const seenKeys = {} for (let i = 0; i < children.length; i++) { const vnode = children[i] const key = vnode.key - - if (key != null) { - if (cache[key]) { - warn(`The duplicate keys: '${key}' that is causing an update error.`) + if (isDef(key)) { + if (seenKeys[key]) { + warn( + `Duplicate keys detected: '${key}'. This may cause an update error.`, + vnode.context + ) } else { - cache[key] = true + seenKeys[key] = true } } } From f9f9c1f7b95faad5b2232b02163f80f6a8dbf50a Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 12 Dec 2017 16:17:11 -0500 Subject: [PATCH 3/3] Update children.spec.js --- test/unit/modules/vdom/patch/children.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/modules/vdom/patch/children.spec.js b/test/unit/modules/vdom/patch/children.spec.js index 1f92eeb2f11..ec4d521955c 100644 --- a/test/unit/modules/vdom/patch/children.spec.js +++ b/test/unit/modules/vdom/patch/children.spec.js @@ -515,7 +515,7 @@ describe('vdom patch: children', () => { const vnode = new VNode('p', {}, ['b', 'a', 'c', 'b'].map(makeNode)) patch(null, vnode) - expect('The duplicate keys: \'b\' that is causing an update error.').toHaveBeenWarned() + expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned() }) it('should warn with duplicate keys: updateChildren', () => { @@ -526,8 +526,8 @@ describe('vdom patch: children', () => { 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('The duplicate keys: \'b\' that is causing an update error.').toHaveBeenWarned() + expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned() patch(vnode2, vnode3) - expect('The duplicate keys: \'b\' that is causing an update error.').toHaveBeenWarned() + expect(`Duplicate keys detected: 'b'`).toHaveBeenWarned() }) })