diff --git a/src/core/instance/render-helpers/resolve-slots.js b/src/core/instance/render-helpers/resolve-slots.js index 024fbbff7a..0065e3c952 100644 --- a/src/core/instance/render-helpers/resolve-slots.js +++ b/src/core/instance/render-helpers/resolve-slots.js @@ -20,7 +20,7 @@ export function resolveSlots ( } // named slots should only be respected if the vnode was rendered in the // same context. - if ((child.context === context || child.functionalContext === context) && + if ((child.context === context || child.fnContext === context) && data && data.slot != null ) { const name = child.data.slot diff --git a/src/core/vdom/create-functional-component.js b/src/core/vdom/create-functional-component.js index d749ab9a7d..ff00f240a2 100644 --- a/src/core/vdom/create-functional-component.js +++ b/src/core/vdom/create-functional-component.js @@ -49,8 +49,8 @@ function FunctionalRenderContext ( this._c = (a, b, c, d) => { const vnode: ?VNode = createElement(contextVm, a, b, c, d, needNormalization) if (vnode) { - vnode.functionalScopeId = options._scopeId - vnode.functionalContext = parent + vnode.fnScopeId = options._scopeId + vnode.fnContext = parent } return vnode } @@ -91,8 +91,8 @@ export function createFunctionalComponent ( const vnode = options.render.call(null, renderContext._c, renderContext) if (vnode instanceof VNode) { - vnode.functionalContext = contextVm - vnode.functionalOptions = options + vnode.fnContext = contextVm + vnode.fnOptions = options if (data.slot) { (vnode.data || (vnode.data = {})).slot = data.slot } diff --git a/src/core/vdom/patch.js b/src/core/vdom/patch.js index f8f2858542..368eb41afc 100644 --- a/src/core/vdom/patch.js +++ b/src/core/vdom/patch.js @@ -294,7 +294,7 @@ export function createPatchFunction (backend) { // of going through the normal attribute patching process. function setScope (vnode) { let i - if (isDef(i = vnode.functionalScopeId)) { + if (isDef(i = vnode.fnScopeId)) { nodeOps.setAttribute(vnode.elm, i, '') } else { let ancestor = vnode @@ -308,7 +308,7 @@ export function createPatchFunction (backend) { // for slot content they should also get the scopeId from the host instance. if (isDef(i = activeInstance) && i !== vnode.context && - i !== vnode.functionalContext && + i !== vnode.fnContext && isDef(i = i.$options._scopeId) ) { nodeOps.setAttribute(vnode.elm, i, '') diff --git a/src/core/vdom/vnode.js b/src/core/vdom/vnode.js index 33b5612fe6..838f9f1e15 100644 --- a/src/core/vdom/vnode.js +++ b/src/core/vdom/vnode.js @@ -24,9 +24,9 @@ export default class VNode { asyncMeta: Object | void; isAsyncPlaceholder: boolean; ssrContext: Object | void; - functionalContext: Component | void; // real context vm for functional nodes - functionalOptions: ?ComponentOptions; // for SSR caching - functionalScopeId: ?string; // functioanl scope id support + fnContext: Component | void; // real context vm for functional nodes + fnOptions: ?ComponentOptions; // for SSR caching + fnScopeId: ?string; // functioanl scope id support constructor ( tag?: string, @@ -45,9 +45,9 @@ export default class VNode { this.elm = elm this.ns = undefined this.context = context - this.functionalContext = undefined - this.functionalOptions = undefined - this.functionalScopeId = undefined + this.fnContext = undefined + this.fnOptions = undefined + this.fnScopeId = undefined this.key = data && data.key this.componentOptions = componentOptions this.componentInstance = undefined @@ -101,6 +101,9 @@ export function cloneVNode (vnode: VNode, deep?: boolean): VNode { cloned.isStatic = vnode.isStatic cloned.key = vnode.key cloned.isComment = vnode.isComment + cloned.fnContext = vnode.fnContext + cloned.fnOptions = vnode.fnOptions + cloned.fnScopeId = vnode.fnScopeId cloned.isCloned = true if (deep) { if (vnode.children) { diff --git a/src/server/render.js b/src/server/render.js index 0ca49f7c2b..0400aa7f3b 100644 --- a/src/server/render.js +++ b/src/server/render.js @@ -252,8 +252,8 @@ function renderElement (el, isRoot, context) { el.data.attrs[SSR_ATTR] = 'true' } - if (el.functionalOptions) { - registerComponentForCache(el.functionalOptions, write) + if (el.fnOptions) { + registerComponentForCache(el.fnOptions, write) } const startTag = renderStartingTag(el, context) diff --git a/test/unit/features/component/component-slot.spec.js b/test/unit/features/component/component-slot.spec.js index 4e6deac39c..28d6555ee6 100644 --- a/test/unit/features/component/component-slot.spec.js +++ b/test/unit/features/component/component-slot.spec.js @@ -779,4 +779,49 @@ describe('Component slot', () => { expect(vm.$el.innerHTML).toBe('
fallback
') }) + + // #7106 + it('should not lose functional slot across renders', done => { + const One = { + data: () => ({ + foo: true + }), + render (h) { + this.foo + return h('div', this.$slots.slot) + } + } + + const Two = { + render (h) { + return h('span', this.$slots.slot) + } + } + + const Three = { + functional: true, + render: (h, { children }) => h('span', children) + } + + const vm = new Vue({ + template: ` +
+ + + hello + + +
+ `, + components: { One, Two, Three } + }).$mount() + + expect(vm.$el.textContent).toBe('hello') + // trigger re-render of + vm.$refs.one.foo = false + waitForUpdate(() => { + // should still be there + expect(vm.$el.textContent).toBe('hello') + }).then(done) + }) })