From d5adf95dda31945a227852a6527e25bd91b561c2 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Thu, 10 Jul 2025 09:46:41 +0800 Subject: [PATCH] feat(runtime-vapor): support functional slot in vdom component (#13576) --- .../runtime-core/src/helpers/renderSlot.ts | 2 +- .../__tests__/vdomInterop.spec.ts | 62 ++++++++++++++++++- packages/runtime-vapor/src/componentProps.ts | 2 +- packages/runtime-vapor/src/vdomInterop.ts | 8 +-- 4 files changed, 66 insertions(+), 8 deletions(-) diff --git a/packages/runtime-core/src/helpers/renderSlot.ts b/packages/runtime-core/src/helpers/renderSlot.ts index 152c5a4b81..d5feae617a 100644 --- a/packages/runtime-core/src/helpers/renderSlot.ts +++ b/packages/runtime-core/src/helpers/renderSlot.ts @@ -35,7 +35,7 @@ export function renderSlot( let slot = slots[name] // vapor slots rendered in vdom - if (slot && slots._vapor) { + if (slot && (slot as any).__vapor) { const ret = (openBlock(), createBlock(VaporSlot, props)) ret.vs = { slot, fallback } return ret diff --git a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts index 08326d4d5d..582e67dd87 100644 --- a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts +++ b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts @@ -1,4 +1,4 @@ -import { defineComponent, h } from '@vue/runtime-dom' +import { createVNode, defineComponent, h, renderSlot } from '@vue/runtime-dom' import { makeInteropRender } from './_utils' import { createComponent, defineVaporComponent } from '../src' @@ -9,7 +9,65 @@ describe('vdomInterop', () => { describe.todo('emit', () => {}) - describe.todo('slots', () => {}) + describe('slots', () => { + test('basic', () => { + const VDomChild = defineComponent({ + setup(_, { slots }) { + return () => renderSlot(slots, 'default') + }, + }) + + const VaporChild = defineVaporComponent({ + setup() { + return createComponent( + VDomChild as any, + null, + { + default: () => document.createTextNode('default slot'), + }, + true, + ) + }, + }) + + const { html } = define({ + setup() { + return () => h(VaporChild as any) + }, + }).render() + + expect(html()).toBe('default slot') + }) + + test('functional slot', () => { + const VDomChild = defineComponent({ + setup(_, { slots }) { + return () => createVNode(slots.default!) + }, + }) + + const VaporChild = defineVaporComponent({ + setup() { + return createComponent( + VDomChild as any, + null, + { + default: () => document.createTextNode('default slot'), + }, + true, + ) + }, + }) + + const { html } = define({ + setup() { + return () => h(VaporChild as any) + }, + }).render() + + expect(html()).toBe('default slot') + }) + }) describe.todo('provide', () => {}) diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index 7ee99b6817..b870a21b2e 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -185,7 +185,7 @@ export function getAttrFromRawProps(rawProps: RawProps, key: string): unknown { source = dynamicSources[i] isDynamic = isFunction(source) source = isDynamic ? (source as Function)() : source - if (hasOwn(source, key)) { + if (source && hasOwn(source, key)) { const value = isDynamic ? source[key] : source[key]() if (merged) { merged.push(value) diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index e277024d73..8c1dd2cee2 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -134,11 +134,11 @@ const vaporSlotPropsProxyHandler: ProxyHandler< const vaporSlotsProxyHandler: ProxyHandler = { get(target, key) { - if (key === '_vapor') { - return target - } else { - return target[key] + const slot = target[key] + if (isFunction(slot)) { + slot.__vapor = true } + return slot }, } -- 2.47.2