Skip to content

Commit 0e8560d

Browse files
committed
fix: expose v-slot slots without scope on this.$slots
fix #9421, fix #9458
1 parent ca57920 commit 0e8560d

File tree

4 files changed

+51
-5
lines changed

4 files changed

+51
-5
lines changed

Diff for: src/compiler/codegen/index.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,9 @@ function genScopedSlot (
411411
: genChildren(el, state) || 'undefined'
412412
: genElement(el, state)
413413
}}`
414-
return `{key:${el.slotTarget || `"default"`},fn:${fn}}`
414+
// reverse proxy v-slot without scope on this.$slots
415+
const reverseProxy = slotScope ? `` : `,proxy:true`
416+
return `{key:${el.slotTarget || `"default"`},fn:${fn}${reverseProxy}}`
415417
}
416418

417419
export function genChildren (

Diff for: src/core/instance/render-helpers/resolve-scoped-slots.js

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ export function resolveScopedSlots (
1111
if (Array.isArray(slot)) {
1212
resolveScopedSlots(slot, hasDynamicKeys, res)
1313
} else if (slot) {
14+
// marker for reverse proxying v-slot without scope on this.$slots
15+
if (slot.proxy) {
16+
slot.fn.proxy = true
17+
}
1418
res[slot.key] = slot.fn
1519
}
1620
}

Diff for: src/core/vdom/helpers/normalize-scoped-slots.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function normalizeScopedSlots (
1616
res = {}
1717
for (const key in slots) {
1818
if (slots[key] && key[0] !== '$') {
19-
res[key] = normalizeScopedSlot(slots[key])
19+
res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
2020
}
2121
}
2222
}
@@ -31,16 +31,27 @@ export function normalizeScopedSlots (
3131
return res
3232
}
3333

34-
function normalizeScopedSlot(fn: Function): Function {
35-
return scope => {
36-
let res = fn(scope)
34+
function normalizeScopedSlot(normalSlots, key, fn) {
35+
const normalized = scope => {
36+
let res = fn(scope || {})
3737
res = res && typeof res === 'object' && !Array.isArray(res)
3838
? [res] // single vnode
3939
: normalizeChildren(res)
4040
return res && res.length === 0
4141
? undefined
4242
: res
4343
}
44+
// this is a slot using the new v-slot syntax without scope. although it is
45+
// compiled as a scoped slot, render fn users would expect it to be present
46+
// on this.$slots because the usage is semantically a normal slot.
47+
if (fn.proxy) {
48+
Object.defineProperty(normalSlots, key, {
49+
get: normalized,
50+
enumerable: true,
51+
configurable: true
52+
})
53+
}
54+
return normalized
4455
}
4556

4657
function proxyNormalSlot(slots, key) {

Diff for: test/unit/features/component/component-scoped-slot.spec.js

+29
Original file line numberDiff line numberDiff line change
@@ -1059,4 +1059,33 @@ describe('Component scoped slot', () => {
10591059

10601060
expect(vm.$el.textContent).toBe(`fallback`)
10611061
})
1062+
1063+
it('should expose v-slot without scope on this.$slots', () => {
1064+
const vm = new Vue({
1065+
template: `<foo><template v-slot>hello</template></foo>`,
1066+
components: {
1067+
foo: {
1068+
render(h) {
1069+
return h('div', this.$slots.default)
1070+
}
1071+
}
1072+
}
1073+
}).$mount()
1074+
expect(vm.$el.textContent).toBe('hello')
1075+
})
1076+
1077+
it('should not expose legacy syntax scoped slot on this.$slots', () => {
1078+
const vm = new Vue({
1079+
template: `<foo><template slot-scope="foo">hello</template></foo>`,
1080+
components: {
1081+
foo: {
1082+
render(h) {
1083+
expect(this.$slots.default).toBeUndefined()
1084+
return h('div', this.$slots.default)
1085+
}
1086+
}
1087+
}
1088+
}).$mount()
1089+
expect(vm.$el.textContent).toBe('')
1090+
})
10621091
})

0 commit comments

Comments
 (0)