Skip to content

Commit 0529040

Browse files
committed
fix: deep clone slot vnodes on re-render
fix #6372
1 parent 172dbf9 commit 0529040

File tree

3 files changed

+55
-5
lines changed

3 files changed

+55
-5
lines changed

src/core/instance/render.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,13 @@ export function renderMixin (Vue: Class<Component>) {
7878
} = vm.$options
7979

8080
if (vm._isMounted) {
81-
// clone slot nodes on re-renders
81+
// if the parent didn't update, the slot nodes will be the ones from
82+
// last render. They need to be cloned to ensure "freshness" for this render.
8283
for (const key in vm.$slots) {
83-
vm.$slots[key] = cloneVNodes(vm.$slots[key])
84+
const slot = vm.$slots[key]
85+
if (slot._rendered) {
86+
vm.$slots[key] = cloneVNodes(slot, true /* deep */)
87+
}
8488
}
8589
}
8690

src/core/vdom/vnode.js

+6-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export function createTextVNode (val: string | number) {
7979
// used for static nodes and slot nodes because they may be reused across
8080
// multiple renders, cloning them avoids errors when DOM manipulations rely
8181
// on their elm reference.
82-
export function cloneVNode (vnode: VNode): VNode {
82+
export function cloneVNode (vnode: VNode, deep?: boolean): VNode {
8383
const cloned = new VNode(
8484
vnode.tag,
8585
vnode.data,
@@ -95,14 +95,17 @@ export function cloneVNode (vnode: VNode): VNode {
9595
cloned.key = vnode.key
9696
cloned.isComment = vnode.isComment
9797
cloned.isCloned = true
98+
if (deep) {
99+
cloned.children = vnode.children && cloneVNodes(vnode.children)
100+
}
98101
return cloned
99102
}
100103

101-
export function cloneVNodes (vnodes: Array<VNode>): Array<VNode> {
104+
export function cloneVNodes (vnodes: Array<VNode>, deep?: boolean): Array<VNode> {
102105
const len = vnodes.length
103106
const res = new Array(len)
104107
for (let i = 0; i < len; i++) {
105-
res[i] = cloneVNode(vnodes[i])
108+
res[i] = cloneVNode(vnodes[i], deep)
106109
}
107110
return res
108111
}

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

+43
Original file line numberDiff line numberDiff line change
@@ -685,4 +685,47 @@ describe('Component slot', () => {
685685
}).$mount()
686686
expect(vm.$el.innerHTML).toBe('<div>default<span>foo</span></div>')
687687
})
688+
689+
it('should handle nested components in slots properly', done => {
690+
const TestComponent = {
691+
template: `
692+
<component :is="toggleEl ? 'b' : 'i'">
693+
<slot />
694+
</component>
695+
`,
696+
data () {
697+
return {
698+
toggleEl: true
699+
}
700+
}
701+
}
702+
703+
const vm = new Vue({
704+
template: `
705+
<div>
706+
<test-component ref="test">
707+
<div>
708+
<foo/>
709+
</div><bar/>
710+
</test-component>
711+
</div>
712+
`,
713+
components: {
714+
TestComponent,
715+
foo: {
716+
template: `<div>foo</div>`
717+
},
718+
bar: {
719+
template: `<div>bar</div>`
720+
}
721+
}
722+
}).$mount()
723+
724+
expect(vm.$el.innerHTML).toBe(`<b><div><div>foo</div></div><div>bar</div></b>`)
725+
726+
vm.$refs.test.toggleEl = false
727+
waitForUpdate(() => {
728+
expect(vm.$el.innerHTML).toBe(`<i><div><div>foo</div></div><div>bar</div></i>`)
729+
}).then(done)
730+
})
688731
})

0 commit comments

Comments
 (0)