From 0e8560d0fc1c0fbf3a52464939701e0e44543b00 Mon Sep 17 00:00:00 2001 From: Evan You Date: Fri, 8 Feb 2019 13:47:17 -0500 Subject: [PATCH] fix: expose v-slot slots without scope on this.$slots fix #9421, fix #9458 --- src/compiler/codegen/index.js | 4 ++- .../render-helpers/resolve-scoped-slots.js | 4 +++ .../vdom/helpers/normalize-scoped-slots.js | 19 +++++++++--- .../component/component-scoped-slot.spec.js | 29 +++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index 0c218869c25..93ae4cbc21f 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -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 ( diff --git a/src/core/instance/render-helpers/resolve-scoped-slots.js b/src/core/instance/render-helpers/resolve-scoped-slots.js index a095865ebf2..2e2abbf8186 100644 --- a/src/core/instance/render-helpers/resolve-scoped-slots.js +++ b/src/core/instance/render-helpers/resolve-scoped-slots.js @@ -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 } } diff --git a/src/core/vdom/helpers/normalize-scoped-slots.js b/src/core/vdom/helpers/normalize-scoped-slots.js index 29e7b6c7f55..c8b48a8dfe1 100644 --- a/src/core/vdom/helpers/normalize-scoped-slots.js +++ b/src/core/vdom/helpers/normalize-scoped-slots.js @@ -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]) } } } @@ -31,9 +31,9 @@ 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) @@ -41,6 +41,17 @@ function normalizeScopedSlot(fn: Function): Function { ? 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) { diff --git a/test/unit/features/component/component-scoped-slot.spec.js b/test/unit/features/component/component-scoped-slot.spec.js index 06a6539dfcf..8e72efd2e7a 100644 --- a/test/unit/features/component/component-scoped-slot.spec.js +++ b/test/unit/features/component/component-scoped-slot.spec.js @@ -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: ``, + 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: ``, + components: { + foo: { + render(h) { + expect(this.$slots.default).toBeUndefined() + return h('div', this.$slots.default) + } + } + } + }).$mount() + expect(vm.$el.textContent).toBe('') + }) })