]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-vapor): fallback component (#232)
authorKevin Deng 三咲智子 <sxzz@sxzz.moe>
Wed, 13 Nov 2024 08:53:44 +0000 (16:53 +0800)
committerGitHub <noreply@github.com>
Wed, 13 Nov 2024 08:53:44 +0000 (16:53 +0800)
* feat(runtime-vapor): fallback component

* fix

packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts
packages/runtime-vapor/src/apiCreateComponent.ts
packages/runtime-vapor/src/componentAttrs.ts
packages/runtime-vapor/src/componentProps.ts

index 0451d2650c434afc3d6c79b10b69b2887c88368b..ac084cca7de4e0ee19f28d272db50b9721c84750 100644 (file)
@@ -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()
index 84218aa76afcc96cdb8557edbb6783cd8b456500..7b91988329c081e37bc769e0c37b0dc493108b0d 100644 (file)
@@ -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
+}
index 5322b5f3208dbd8522c2d5a880fde6a87b61a058..53f951299a9f530a302df1e6af81a82efa273270 100644 (file)
@@ -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]
index 549734b33177437fa12913cd4b8f42232be57984..6e23328812449091a475d8da19bc3ce781c3a02f 100644 (file)
@@ -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)
 }