Skip to content

Commit

Permalink
fix: deep clone slot vnodes on re-render
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 authored and ztlevi committed Feb 14, 2018
1 parent 39b3e64 commit bd5386a
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 5 deletions.
8 changes: 6 additions & 2 deletions src/core/instance/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,13 @@ export function renderMixin (Vue: Class<Component>) {
} = vm.$options

if (vm._isMounted) {
// clone slot nodes on re-renders
// if the parent didn't update, the slot nodes will be the ones from
// last render. They need to be cloned to ensure "freshness" for this render.
for (const key in vm.$slots) {
vm.$slots[key] = cloneVNodes(vm.$slots[key])
const slot = vm.$slots[key]
if (slot._rendered) {
vm.$slots[key] = cloneVNodes(slot, true /* deep */)
}
}
}

Expand Down
9 changes: 6 additions & 3 deletions src/core/vdom/vnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export function createTextVNode (val: string | number) {
// used for static nodes and slot nodes because they may be reused across
// multiple renders, cloning them avoids errors when DOM manipulations rely
// on their elm reference.
export function cloneVNode (vnode: VNode): VNode {
export function cloneVNode (vnode: VNode, deep?: boolean): VNode {
const cloned = new VNode(
vnode.tag,
vnode.data,
Expand All @@ -95,14 +95,17 @@ export function cloneVNode (vnode: VNode): VNode {
cloned.key = vnode.key
cloned.isComment = vnode.isComment
cloned.isCloned = true
if (deep) {
cloned.children = vnode.children && cloneVNodes(vnode.children)
}
return cloned
}

export function cloneVNodes (vnodes: Array<VNode>): Array<VNode> {
export function cloneVNodes (vnodes: Array<VNode>, deep?: boolean): Array<VNode> {
const len = vnodes.length
const res = new Array(len)
for (let i = 0; i < len; i++) {
res[i] = cloneVNode(vnodes[i])
res[i] = cloneVNode(vnodes[i], deep)
}
return res
}
43 changes: 43 additions & 0 deletions test/unit/features/component/component-slot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -685,4 +685,47 @@ describe('Component slot', () => {
}).$mount()
expect(vm.$el.innerHTML).toBe('<div>default<span>foo</span></div>')
})

it('should handle nested components in slots properly', done => {
const TestComponent = {
template: `
<component :is="toggleEl ? 'b' : 'i'">
<slot />
</component>
`,
data () {
return {
toggleEl: true
}
}
}

const vm = new Vue({
template: `
<div>
<test-component ref="test">
<div>
<foo/>
</div><bar/>
</test-component>
</div>
`,
components: {
TestComponent,
foo: {
template: `<div>foo</div>`
},
bar: {
template: `<div>bar</div>`
}
}
}).$mount()

expect(vm.$el.innerHTML).toBe(`<b><div><div>foo</div></div><div>bar</div></b>`)

vm.$refs.test.toggleEl = false
waitForUpdate(() => {
expect(vm.$el.innerHTML).toBe(`<i><div><div>foo</div></div><div>bar</div></i>`)
}).then(done)
})
})

0 comments on commit bd5386a

Please sign in to comment.