setup() {
const FooBar = resolveComponent('foo-bar')
const BarBaz = resolveComponent('bar-baz')
- // @ts-expect-error TODO support string
return [createComponent(FooBar), createComponent(BarBaz)]
},
}).create()
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,
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
+}
-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 {
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]
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
}
}
+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)
}