From c91586528a7b98e2642dfab0b36db94b47cbe450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Deng=20=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90?= Date: Wed, 13 Nov 2024 16:53:44 +0800 Subject: [PATCH] feat(runtime-vapor): fallback component (#232) * feat(runtime-vapor): fallback component * fix --- .../__tests__/apiCreateVaporApp.spec.ts | 1 - .../runtime-vapor/src/apiCreateComponent.ts | 52 +++++++++++++++++-- packages/runtime-vapor/src/componentAttrs.ts | 17 ++---- packages/runtime-vapor/src/componentProps.ts | 29 +++++++++-- 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts index 0451d2650..ac084cca7 100644 --- a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts @@ -134,7 +134,6 @@ describe('api: createVaporApp', () => { setup() { const FooBar = resolveComponent('foo-bar') const BarBaz = resolveComponent('bar-baz') - // @ts-expect-error TODO support string return [createComponent(FooBar), createComponent(BarBaz)] }, }).create() diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 84218aa76..7b9198832 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -5,17 +5,30 @@ import { currentInstance, } from './component' import { setupComponent } from './apiRender' -import type { RawProps } from './componentProps' -import type { RawSlots } from './componentSlots' +import { + type NormalizedRawProps, + type RawProps, + normalizeRawProps, + walkRawProps, +} from './componentProps' +import { type RawSlots, isDynamicSlotFn } from './componentSlots' import { withAttrs } from './componentAttrs' +import { isString } from '@vue/shared' +import { renderEffect } from './renderEffect' +import { normalizeBlock } from './dom/element' +import { setDynamicProp } from './dom/prop' export function createComponent( - comp: Component, + comp: Component | string, rawProps: RawProps | null = null, slots: RawSlots | null = null, singleRoot: boolean = false, once: boolean = false, -): ComponentInternalInstance { +): ComponentInternalInstance | HTMLElement { + if (isString(comp)) { + return fallbackComponent(comp, rawProps, slots) + } + const current = currentInstance! const instance = createComponentInstance( comp, @@ -30,3 +43,34 @@ export function createComponent( return instance } + +function fallbackComponent( + comp: string, + rawProps: RawProps | null, + slots: RawSlots | null, +): HTMLElement { + // eslint-disable-next-line no-restricted-globals + const el = document.createElement(comp) + + if (rawProps) { + rawProps = normalizeRawProps(rawProps) + renderEffect(() => { + walkRawProps(rawProps as NormalizedRawProps, (key, value, getter) => { + setDynamicProp(el, key, getter ? value() : value) + }) + }) + } + + if (slots) { + if (!Array.isArray(slots)) slots = [slots] + for (let i = 0; i < slots.length; i++) { + const slot = slots[i] + if (!isDynamicSlotFn(slot) && slot.default) { + const block = slot.default && slot.default() + if (block) el.append(...normalizeBlock(block)) + } + } + } + + return el +} diff --git a/packages/runtime-vapor/src/componentAttrs.ts b/packages/runtime-vapor/src/componentAttrs.ts index 5322b5f32..53f951299 100644 --- a/packages/runtime-vapor/src/componentAttrs.ts +++ b/packages/runtime-vapor/src/componentAttrs.ts @@ -1,8 +1,8 @@ -import { camelize, isArray, isFunction } from '@vue/shared' +import { camelize, isArray } from '@vue/shared' import { type ComponentInternalInstance, currentInstance } from './component' import { isEmitListener } from './componentEmits' import { setDynamicProps } from './dom/prop' -import type { RawProps } from './componentProps' +import { type RawProps, walkRawProps } from './componentProps' import { renderEffect } from './renderEffect' export function patchAttrs(instance: ComponentInternalInstance): void { @@ -14,19 +14,8 @@ export function patchAttrs(instance: ComponentInternalInstance): void { if (!rawProps.length) return const keys = new Set() - for (const props of Array.from(rawProps).reverse()) { - if (isFunction(props)) { - const resolved = props() - for (const rawKey in resolved) { - registerAttr(rawKey, resolved[rawKey]) - } - } else { - for (const rawKey in props) { - registerAttr(rawKey, props[rawKey], true) - } - } - } + walkRawProps(rawProps, registerAttr) for (const key in attrs) { if (!keys.has(key)) { delete attrs[key] diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index 549734b33..6e2332881 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -83,10 +83,7 @@ export function initProps( isStateful: boolean, once: boolean, ): void { - if (!rawProps) rawProps = [] - else if (!isArray(rawProps)) rawProps = [rawProps] - instance.rawProps = rawProps - + instance.rawProps = rawProps = normalizeRawProps(rawProps) const props: Data = {} const attrs = (instance.attrs = shallowReactive({})) const [options] = instance.propsOptions @@ -166,6 +163,30 @@ function registerProp( } } +export function normalizeRawProps(rawProps: RawProps): NormalizedRawProps { + if (!rawProps) return [] + if (!isArray(rawProps)) return [rawProps] + return rawProps +} + +export function walkRawProps( + rawProps: NormalizedRawProps, + cb: (key: string, value: any, getter?: boolean) => void, +): void { + for (const props of Array.from(rawProps).reverse()) { + if (isFunction(props)) { + const resolved = props() + for (const rawKey in resolved) { + cb(rawKey, resolved[rawKey]) + } + } else { + for (const rawKey in props) { + cb(rawKey, props[rawKey], true) + } + } + } +} + function getRawKey(obj: Data, key: string) { return Object.keys(obj).find(k => camelize(k) === key) }