Skip to content

Commit 530ca1b

Browse files
committed
fix(core): properly handle reused vnodes
This also removes the restrictions on rendering the same slot multiple times. close #7913
1 parent 097f622 commit 530ca1b

File tree

4 files changed

+56
-62
lines changed

4 files changed

+56
-62
lines changed

src/core/instance/render-helpers/render-slot.js

+1-13
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,7 @@ export function renderSlot (
2626
}
2727
nodes = scopedSlotFn(props) || fallback
2828
} else {
29-
const slotNodes = this.$slots[name]
30-
// warn duplicate slot usage
31-
if (slotNodes) {
32-
if (process.env.NODE_ENV !== 'production' && slotNodes._rendered) {
33-
warn(
34-
`Duplicate presence of slot "${name}" found in the same render tree ` +
35-
`- this will likely cause render errors.`,
36-
this
37-
)
38-
}
39-
slotNodes._rendered = true
40-
}
41-
nodes = slotNodes || fallback
29+
nodes = this.$slots[name] || fallback
4230
}
4331

4432
const target = props && props.slot

src/core/instance/render.js

-8
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,6 @@ export function renderMixin (Vue: Class<Component>) {
6262
const vm: Component = this
6363
const { render, _parentVnode } = vm.$options
6464

65-
// reset _rendered flag on slots for duplicate slot check
66-
if (process.env.NODE_ENV !== 'production') {
67-
for (const key in vm.$slots) {
68-
// $flow-disable-line
69-
vm.$slots[key]._rendered = false
70-
}
71-
}
72-
7365
if (_parentVnode) {
7466
vm.$scopedSlots = _parentVnode.data.scopedSlots || emptyObject
7567
}

src/core/vdom/patch.js

+19-7
Original file line numberDiff line numberDiff line change
@@ -434,20 +434,20 @@ export function createPatchFunction (backend) {
434434
} else if (isUndef(oldEndVnode)) {
435435
oldEndVnode = oldCh[--oldEndIdx]
436436
} else if (sameVnode(oldStartVnode, newStartVnode)) {
437-
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue)
437+
patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
438438
oldStartVnode = oldCh[++oldStartIdx]
439439
newStartVnode = newCh[++newStartIdx]
440440
} else if (sameVnode(oldEndVnode, newEndVnode)) {
441-
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue)
441+
patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
442442
oldEndVnode = oldCh[--oldEndIdx]
443443
newEndVnode = newCh[--newEndIdx]
444444
} else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right
445-
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue)
445+
patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)
446446
canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))
447447
oldStartVnode = oldCh[++oldStartIdx]
448448
newEndVnode = newCh[--newEndIdx]
449449
} else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left
450-
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue)
450+
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
451451
canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)
452452
oldEndVnode = oldCh[--oldEndIdx]
453453
newStartVnode = newCh[++newStartIdx]
@@ -461,7 +461,7 @@ export function createPatchFunction (backend) {
461461
} else {
462462
vnodeToMove = oldCh[idxInOld]
463463
if (sameVnode(vnodeToMove, newStartVnode)) {
464-
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue)
464+
patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)
465465
oldCh[idxInOld] = undefined
466466
canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)
467467
} else {
@@ -505,11 +505,23 @@ export function createPatchFunction (backend) {
505505
}
506506
}
507507

508-
function patchVnode (oldVnode, vnode, insertedVnodeQueue, removeOnly) {
508+
function patchVnode (
509+
oldVnode,
510+
vnode,
511+
insertedVnodeQueue,
512+
ownerArray,
513+
index,
514+
removeOnly
515+
) {
509516
if (oldVnode === vnode) {
510517
return
511518
}
512519

520+
if (isDef(vnode.elm) && isDef(ownerArray)) {
521+
// clone reused vnode
522+
vnode = ownerArray[index] = cloneVNode(vnode)
523+
}
524+
513525
const elm = vnode.elm = oldVnode.elm
514526

515527
if (isTrue(oldVnode.isAsyncPlaceholder)) {
@@ -709,7 +721,7 @@ export function createPatchFunction (backend) {
709721
const isRealElement = isDef(oldVnode.nodeType)
710722
if (!isRealElement && sameVnode(oldVnode, vnode)) {
711723
// patch existing root node
712-
patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly)
724+
patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)
713725
} else {
714726
if (isRealElement) {
715727
// mounting to a real element

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

+36-34
Original file line numberDiff line numberDiff line change
@@ -482,44 +482,46 @@ describe('Component slot', () => {
482482
}).then(done)
483483
})
484484

485-
it('warn duplicate slots', () => {
486-
new Vue({
487-
template: `<div>
488-
<test>
489-
<div>foo</div>
490-
<div slot="a">bar</div>
491-
</test>
492-
</div>`,
493-
components: {
494-
test: {
495-
template: `<div>
496-
<slot></slot><slot></slot>
497-
<div v-for="i in 3"><slot name="a"></slot></div>
498-
</div>`
499-
}
500-
}
501-
}).$mount()
502-
expect('Duplicate presence of slot "default"').toHaveBeenWarned()
503-
expect('Duplicate presence of slot "a"').toHaveBeenWarned()
504-
})
505-
506-
it('should not warn valid conditional slots', () => {
507-
new Vue({
508-
template: `<div>
509-
<test>
510-
<div>foo</div>
511-
</test>
512-
</div>`,
485+
it('should support duplicate slots', done => {
486+
const vm = new Vue({
487+
template: `
488+
<foo ref="foo">
489+
<div slot="a">{{ n }}</div>
490+
</foo>
491+
`,
492+
data: {
493+
n: 1
494+
},
513495
components: {
514-
test: {
515-
template: `<div>
516-
<slot v-if="true"></slot>
517-
<slot v-else></slot>
518-
</div>`
496+
foo: {
497+
data() {
498+
return { ok: true }
499+
},
500+
template: `
501+
<div>
502+
<slot name="a" />
503+
<slot v-if="ok" name="a" />
504+
<pre><slot name="a" /></pre>
505+
</div>
506+
`
519507
}
520508
}
521509
}).$mount()
522-
expect('Duplicate presence of slot "default"').not.toHaveBeenWarned()
510+
expect(vm.$el.innerHTML).toBe(`<div>1</div> <div>1</div> <pre><div>1</div></pre>`)
511+
vm.n++
512+
waitForUpdate(() => {
513+
expect(vm.$el.innerHTML).toBe(`<div>2</div> <div>2</div> <pre><div>2</div></pre>`)
514+
vm.n++
515+
}).then(() => {
516+
expect(vm.$el.innerHTML).toBe(`<div>3</div> <div>3</div> <pre><div>3</div></pre>`)
517+
vm.$refs.foo.ok = false
518+
}).then(() => {
519+
expect(vm.$el.innerHTML).toBe(`<div>3</div> <!----> <pre><div>3</div></pre>`)
520+
vm.n++
521+
vm.$refs.foo.ok = true
522+
}).then(() => {
523+
expect(vm.$el.innerHTML).toBe(`<div>4</div> <div>4</div> <pre><div>4</div></pre>`)
524+
}).then(done)
523525
})
524526

525527
// #3518

0 commit comments

Comments
 (0)