Bläddra i källkod

fix(runtime-vapor): pass render args to template-only components (#14721)

edison 1 vecka sedan
förälder
incheckning
67114e647c

+ 40 - 13
packages/runtime-vapor/__tests__/component.spec.ts

@@ -24,7 +24,7 @@ import {
   template,
   txt,
 } from '../src'
-import { makeRender } from './_utils'
+import { compileToVaporRender, makeRender } from './_utils'
 import type { VaporComponentInstance } from '../src/component'
 import { setElementText, setText } from '../src/dom/prop'
 
@@ -488,20 +488,47 @@ describe('component', () => {
 
   test('should mount component only with template in production mode', () => {
     __DEV__ = false
-    const { component: Child } = define({
-      render() {
-        return template('<div> HI </div>', true)()
-      },
-    })
+    try {
+      const { component: Child } = define({
+        render() {
+          return template('<div> HI </div>', true)()
+        },
+      })
 
-    const { host } = define({
-      setup() {
-        return createComponent(Child, null, null, true)
-      },
-    }).render()
+      const { host } = define({
+        setup() {
+          return createComponent(Child, null, null, true)
+        },
+      }).render()
 
-    expect(host.innerHTML).toBe('<div> HI </div>')
-    __DEV__ = true
+      expect(host.innerHTML).toBe('<div> HI </div>')
+    } finally {
+      __DEV__ = true
+    }
+  })
+
+  test('should pass slot args to template-only component render in production mode', () => {
+    __DEV__ = false
+    try {
+      const { component: Child } = define({
+        render: compileToVaporRender(
+          `<span v-if="$slots.default"><slot /></span>`,
+          { bindingMetadata: {} },
+        ),
+      })
+
+      const { host } = define({
+        setup() {
+          return createComponent(Child, null, {
+            default: () => template('<button>slot</button>')(),
+          })
+        },
+      }).render()
+
+      expect(host.innerHTML).toBe('<span><button>slot</button></span>')
+    } finally {
+      __DEV__ = true
+    }
   })
 
   it('warn if functional vapor component not return a block', () => {

+ 16 - 17
packages/runtime-vapor/src/component.ts

@@ -538,24 +538,27 @@ function createDevSetupStateProxy(
   })
 }
 
+function callRender(
+  render: NonNullable<VaporComponentOptions['render']>,
+  instance: VaporComponentInstance,
+  setupState: Record<string, any>,
+) {
+  return callWithErrorHandling(render, instance, ErrorCodes.RENDER_FUNCTION, [
+    setupState,
+    instance.props,
+    instance.emit,
+    instance.attrs,
+    instance.slots,
+  ])
+}
+
 /**
  * dev only
  */
 export function devRender(instance: VaporComponentInstance): void {
   instance.block =
     (instance.type.render
-      ? callWithErrorHandling(
-          instance.type.render,
-          instance,
-          ErrorCodes.RENDER_FUNCTION,
-          [
-            instance.setupState,
-            instance.props,
-            instance.emit,
-            instance.attrs,
-            instance.slots,
-          ],
-        )
+      ? callRender(instance.type.render, instance, instance.setupState!)
       : callWithErrorHandling(
           isFunction(instance.type) ? instance.type : instance.type.setup!,
           instance,
@@ -1143,11 +1146,7 @@ function handleSetupResult(
     // component has a render function but no setup function
     // (typically components with only a template and no state)
     if (setupResult === EMPTY_OBJ && component.render) {
-      instance.block = callWithErrorHandling(
-        component.render,
-        instance,
-        ErrorCodes.RENDER_FUNCTION,
-      )
+      instance.block = callRender(component.render, instance, setupResult)
     } else {
       // in prod result can only be block
       instance.block = setupResult as Block