Skip to content

Commit

Permalink
fix: ensure functionalContext is cloned during slot clones
Browse files Browse the repository at this point in the history
fix #7106
  • Loading branch information
yyx990803 committed Nov 22, 2017
1 parent 3932a45 commit 604e081
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/core/instance/render-helpers/resolve-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions src/core/vdom/create-functional-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/vdom/patch.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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, '')
Expand Down
15 changes: 9 additions & 6 deletions src/core/vdom/vnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/server/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
45 changes: 45 additions & 0 deletions test/unit/features/component/component-slot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -779,4 +779,49 @@ describe('Component slot', () => {

expect(vm.$el.innerHTML).toBe('<div class="foo"><div class="bar">fallback</div></div>')
})

// #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: `
<div>
<one ref="one">
<two slot="slot">
<three slot="slot">hello</three>
</two>
</one>
</div>
`,
components: { One, Two, Three }
}).$mount()

expect(vm.$el.textContent).toBe('hello')
// trigger re-render of <one>
vm.$refs.one.foo = false
waitForUpdate(() => {
// should still be there
expect(vm.$el.textContent).toBe('hello')
}).then(done)
})
})

0 comments on commit 604e081

Please sign in to comment.