Jelajahi Sumber

feat(runtime-vapor): fallback component (#232)

* feat(runtime-vapor): fallback component

* fix
Kevin Deng 三咲智子 1 tahun lalu
induk
melakukan
c91586528a

+ 0 - 1
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()

+ 48 - 4
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
+}

+ 3 - 14
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<string>()
-  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]

+ 25 - 4
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<Data>({}))
   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)
 }