Przeglądaj źródła

fix: named slots for nested functional components

Named slots should be respecred when passing raw children down multiple
layers of functional components.

fix #7710
Evan You 8 lat temu
rodzic
commit
6dd73e9ee4

+ 16 - 6
src/core/vdom/create-functional-component.js

@@ -10,6 +10,7 @@ import { installRenderHelpers } from '../instance/render-helpers/index'
 import {
   isDef,
   isTrue,
+  hasOwn,
   camelize,
   emptyObject,
   validateProp
@@ -23,6 +24,21 @@ export function FunctionalRenderContext (
   Ctor: Class<Component>
 ) {
   const options = Ctor.options
+  // ensure the createElement function in functional components
+  // gets a unique context - this is necessary for correct named slot check
+  let contextVm
+  if (hasOwn(parent, '_uid')) {
+    contextVm = Object.create(parent)
+    // $flow-disable-line
+    contextVm._original = parent
+  } else {
+    contextVm = parent
+    // $flow-disable-line
+    parent = parent._original
+  }
+  const isCompiled = isTrue(options._compiled)
+  const needNormalization = !isCompiled
+
   this.data = data
   this.props = props
   this.children = children
@@ -31,12 +47,6 @@ export function FunctionalRenderContext (
   this.injections = resolveInject(options.inject, parent)
   this.slots = () => resolveSlots(children, parent)
 
-  // ensure the createElement function in functional components
-  // gets a unique context - this is necessary for correct named slot check
-  const contextVm = Object.create(parent)
-  const isCompiled = isTrue(options._compiled)
-  const needNormalization = !isCompiled
-
   // support for compiled functional template
   if (isCompiled) {
     // exposing $options for renderStatic()

+ 31 - 0
test/unit/features/component/component-slot.spec.js

@@ -824,4 +824,35 @@ describe('Component slot', () => {
       expect(vm.$el.textContent).toBe('hello')
     }).then(done)
   })
+
+  it('should allow passing named slots as raw children down multiple layers of functional component', () => {
+    const CompB = {
+      functional: true,
+      render (h, { slots }) {
+        return slots().foo
+      }
+    }
+
+    const CompA = {
+      functional: true,
+      render (h, { children }) {
+        return h(CompB, children)
+      }
+    }
+
+    const vm = new Vue({
+      components: {
+        CompA
+      },
+      template: `
+        <div>
+          <comp-a>
+            <span slot="foo">foo</span>
+          </comp-a>
+        </div>
+      `
+    }).$mount()
+
+    expect(vm.$el.textContent).toBe('foo')
+  })
 })