Skip to content

Commit

Permalink
fix: expose v-slot slots without scope on this.$slots
Browse files Browse the repository at this point in the history
fix #9421, fix #9458
  • Loading branch information
yyx990803 committed Feb 8, 2019
1 parent ca57920 commit 0e8560d
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 5 deletions.
4 changes: 3 additions & 1 deletion src/compiler/codegen/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,9 @@ function genScopedSlot (
: genChildren(el, state) || 'undefined'
: genElement(el, state)
}}`
return `{key:${el.slotTarget || `"default"`},fn:${fn}}`
// reverse proxy v-slot without scope on this.$slots
const reverseProxy = slotScope ? `` : `,proxy:true`
return `{key:${el.slotTarget || `"default"`},fn:${fn}${reverseProxy}}`
}

export function genChildren (
Expand Down
4 changes: 4 additions & 0 deletions src/core/instance/render-helpers/resolve-scoped-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export function resolveScopedSlots (
if (Array.isArray(slot)) {
resolveScopedSlots(slot, hasDynamicKeys, res)
} else if (slot) {
// marker for reverse proxying v-slot without scope on this.$slots
if (slot.proxy) {
slot.fn.proxy = true
}
res[slot.key] = slot.fn
}
}
Expand Down
19 changes: 15 additions & 4 deletions src/core/vdom/helpers/normalize-scoped-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function normalizeScopedSlots (
res = {}
for (const key in slots) {
if (slots[key] && key[0] !== '$') {
res[key] = normalizeScopedSlot(slots[key])
res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
}
}
}
Expand All @@ -31,16 +31,27 @@ export function normalizeScopedSlots (
return res
}

function normalizeScopedSlot(fn: Function): Function {
return scope => {
let res = fn(scope)
function normalizeScopedSlot(normalSlots, key, fn) {
const normalized = scope => {
let res = fn(scope || {})
res = res && typeof res === 'object' && !Array.isArray(res)
? [res] // single vnode
: normalizeChildren(res)
return res && res.length === 0
? undefined
: res
}
// this is a slot using the new v-slot syntax without scope. although it is
// compiled as a scoped slot, render fn users would expect it to be present
// on this.$slots because the usage is semantically a normal slot.
if (fn.proxy) {
Object.defineProperty(normalSlots, key, {
get: normalized,
enumerable: true,
configurable: true
})
}
return normalized
}

function proxyNormalSlot(slots, key) {
Expand Down
29 changes: 29 additions & 0 deletions test/unit/features/component/component-scoped-slot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,4 +1059,33 @@ describe('Component scoped slot', () => {

expect(vm.$el.textContent).toBe(`fallback`)
})

it('should expose v-slot without scope on this.$slots', () => {
const vm = new Vue({
template: `<foo><template v-slot>hello</template></foo>`,
components: {
foo: {
render(h) {
return h('div', this.$slots.default)
}
}
}
}).$mount()
expect(vm.$el.textContent).toBe('hello')
})

it('should not expose legacy syntax scoped slot on this.$slots', () => {
const vm = new Vue({
template: `<foo><template slot-scope="foo">hello</template></foo>`,
components: {
foo: {
render(h) {
expect(this.$slots.default).toBeUndefined()
return h('div', this.$slots.default)
}
}
}
}).$mount()
expect(vm.$el.textContent).toBe('')
})
})

0 comments on commit 0e8560d

Please sign in to comment.