From 0529040c17b8632032a43d142aac88386f6b4a1f Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 30 Aug 2017 00:47:10 +0200 Subject: [PATCH] fix: deep clone slot vnodes on re-render fix #6372 --- src/core/instance/render.js | 8 +++- src/core/vdom/vnode.js | 9 ++-- .../features/component/component-slot.spec.js | 43 +++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/core/instance/render.js b/src/core/instance/render.js index 62b4878195..15bc9a3019 100644 --- a/src/core/instance/render.js +++ b/src/core/instance/render.js @@ -78,9 +78,13 @@ export function renderMixin (Vue: Class) { } = 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 */) + } } } diff --git a/src/core/vdom/vnode.js b/src/core/vdom/vnode.js index 7e82ec2762..751e1414bf 100644 --- a/src/core/vdom/vnode.js +++ b/src/core/vdom/vnode.js @@ -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, @@ -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): Array { +export function cloneVNodes (vnodes: Array, deep?: boolean): Array { 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 } diff --git a/test/unit/features/component/component-slot.spec.js b/test/unit/features/component/component-slot.spec.js index 6eb3927ef0..894dc9f7ed 100644 --- a/test/unit/features/component/component-slot.spec.js +++ b/test/unit/features/component/component-slot.spec.js @@ -685,4 +685,47 @@ describe('Component slot', () => { }).$mount() expect(vm.$el.innerHTML).toBe('
defaultfoo
') }) + + it('should handle nested components in slots properly', done => { + const TestComponent = { + template: ` + + + + `, + data () { + return { + toggleEl: true + } + } + } + + const vm = new Vue({ + template: ` +
+ +
+ +
+
+
+ `, + components: { + TestComponent, + foo: { + template: `
foo
` + }, + bar: { + template: `
bar
` + } + } + }).$mount() + + expect(vm.$el.innerHTML).toBe(`
foo
bar
`) + + vm.$refs.test.toggleEl = false + waitForUpdate(() => { + expect(vm.$el.innerHTML).toBe(`
foo
bar
`) + }).then(done) + }) })