Skip to content

Commit

Permalink
fix(core): warn duplicate keys in all cases (vuejs#7200)
Browse files Browse the repository at this point in the history
  • Loading branch information
gebilaoxiong authored and lovelope committed Feb 1, 2018
1 parent 20c942b commit 75d47f4
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 8 deletions.
32 changes: 25 additions & 7 deletions src/core/vdom/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion test/unit/features/component/component.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ describe('Component', () => {
const vm = new Vue({
template:
'<div>' +
'<component v-for="c in comps" :key="c.type" :is="c.type"></component>' +
'<component v-for="(c, i) in comps" :key="i" :is="c.type"></component>' +
'</div>',
data: {
comps: [{ type: 'one' }, { type: 'two' }]
Expand Down
23 changes: 23 additions & 0 deletions test/unit/modules/vdom/patch/children.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})

0 comments on commit 75d47f4

Please sign in to comment.