2
0
Эх сурвалжийг харах

fix: expose v-slot slots without scope on this.$slots

fix #9421, fix #9458
Evan You 7 жил өмнө
parent
commit
0e8560d0fc

+ 3 - 1
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 (

+ 4 - 0
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
     }
   }

+ 15 - 4
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) {

+ 29 - 0
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: `<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('')
+  })
 })