From afafd839ce7cb37c01fc6cca1c68376d27bf54b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Mon, 17 Jun 2024 11:40:48 +0800 Subject: [PATCH 1/2] feat(runtime-vapor): fallback component --- .../__tests__/apiCreateVaporApp.spec.ts | 1 - .../runtime-vapor/src/apiCreateComponent.ts | 59 ++++++++++++++++++- packages/runtime-vapor/src/componentAttrs.ts | 17 +----- packages/runtime-vapor/src/componentProps.ts | 29 +++++++-- 4 files changed, 84 insertions(+), 22 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..97693d24b 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -4,18 +4,31 @@ import { createComponentInstance, currentInstance, } from './component' -import { setupComponent } from './apiRender' -import type { RawProps } from './componentProps' +import { type Block, setupComponent } from './apiRender' +import { + type NormalizedRawProps, + type RawProps, + normalizeRawProps, + walkRawProps, +} from './componentProps' import type { RawSlots } 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 { + if (isString(comp)) { + return fallbackComponent(comp, rawProps, slots, singleRoot) + } + const current = currentInstance! const instance = createComponentInstance( comp, @@ -30,3 +43,43 @@ export function createComponent( return instance } + +function fallbackComponent( + comp: string, + rawProps: RawProps | null, + slots: RawSlots | null, + singleRoot: boolean, +) { + // 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 && slots.length) { + renderEffect(() => { + let block: Block | undefined + + if (slots && slots.default) { + block = slots.default() + } else { + for (const slotFn of dynamicSlots!) { + const slot = slotFn() + if (slot.name === 'default') { + block = slot.fn() + break + } + } + } + + if (block) el.append(...normalizeBlock(block)) + }) + } + return { __return: el, rawProps } +} 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..8ef65e70f 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) { + if (!rawProps) return [] + if (!isArray(rawProps)) return [rawProps] + return rawProps +} + +export function walkRawProps( + rawProps: NormalizedRawProps, + cb: (key: string, value: any, getter?: boolean) => 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) } From 2a8b9c363ad3d9a2c5700cb3b47324f0668590fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E6=99=BA=E5=AD=90=20Kevin=20Deng?= Date: Wed, 13 Nov 2024 16:35:41 +0800 Subject: [PATCH 2/2] fix --- .../runtime-vapor/src/apiCreateComponent.ts | 39 +++++++------------ packages/runtime-vapor/src/componentProps.ts | 4 +- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts index 97693d24b..7b9198832 100644 --- a/packages/runtime-vapor/src/apiCreateComponent.ts +++ b/packages/runtime-vapor/src/apiCreateComponent.ts @@ -4,14 +4,14 @@ import { createComponentInstance, currentInstance, } from './component' -import { type Block, setupComponent } from './apiRender' +import { setupComponent } from './apiRender' import { type NormalizedRawProps, type RawProps, normalizeRawProps, walkRawProps, } from './componentProps' -import type { RawSlots } from './componentSlots' +import { type RawSlots, isDynamicSlotFn } from './componentSlots' import { withAttrs } from './componentAttrs' import { isString } from '@vue/shared' import { renderEffect } from './renderEffect' @@ -24,9 +24,9 @@ export function createComponent( slots: RawSlots | null = null, singleRoot: boolean = false, once: boolean = false, -): ComponentInternalInstance { +): ComponentInternalInstance | HTMLElement { if (isString(comp)) { - return fallbackComponent(comp, rawProps, slots, singleRoot) + return fallbackComponent(comp, rawProps, slots) } const current = currentInstance! @@ -48,8 +48,7 @@ function fallbackComponent( comp: string, rawProps: RawProps | null, slots: RawSlots | null, - singleRoot: boolean, -) { +): HTMLElement { // eslint-disable-next-line no-restricted-globals const el = document.createElement(comp) @@ -62,24 +61,16 @@ function fallbackComponent( }) } - if (slots && slots.length) { - renderEffect(() => { - let block: Block | undefined - - if (slots && slots.default) { - block = slots.default() - } else { - for (const slotFn of dynamicSlots!) { - const slot = slotFn() - if (slot.name === 'default') { - block = slot.fn() - break - } - } + 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)) } - - if (block) el.append(...normalizeBlock(block)) - }) + } } - return { __return: el, rawProps } + + return el } diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts index 8ef65e70f..6e2332881 100644 --- a/packages/runtime-vapor/src/componentProps.ts +++ b/packages/runtime-vapor/src/componentProps.ts @@ -163,7 +163,7 @@ function registerProp( } } -export function normalizeRawProps(rawProps: RawProps) { +export function normalizeRawProps(rawProps: RawProps): NormalizedRawProps { if (!rawProps) return [] if (!isArray(rawProps)) return [rawProps] return rawProps @@ -172,7 +172,7 @@ export function normalizeRawProps(rawProps: 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()