wip: component with fallback

This commit is contained in:
Evan You 2024-12-06 11:10:35 +08:00
parent 300bb0859a
commit ae5ec075ad
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
4 changed files with 78 additions and 23 deletions

View File

@ -54,7 +54,9 @@ export function genCreateComponent(
NEWLINE,
`const n${operation.id} = `,
...genCall(
vaporHelper('createComponent'),
operation.asset
? vaporHelper('createComponentWithFallback')
: vaporHelper('createComponent'),
tag,
rawProps,
rawSlots,

View File

@ -19,17 +19,19 @@ import {
} from '@vue/runtime-dom'
import { type Block, isBlock } from './block'
import { pauseTracking, resetTracking } from '@vue/reactivity'
import { EMPTY_OBJ, isFunction } from '@vue/shared'
import { EMPTY_OBJ, isFunction, isString } from '@vue/shared'
import {
type RawProps,
getPropsProxyHandlers,
hasFallthroughAttrs,
normalizePropsOptions,
resolveDynamicProps,
setupPropsValidation,
} from './componentProps'
import { setDynamicProp } from './dom/prop'
import { renderEffect } from './renderEffect'
import { emit, normalizeEmitsOptions } from './componentEmits'
import { setStyle } from './dom/style'
import { setClass, setDynamicProp } from './dom/prop'
export { currentInstance } from '@vue/runtime-dom'
@ -266,7 +268,7 @@ export function isVaporComponent(
export class SetupContext<E = EmitsOptions> {
attrs: Record<string, any>
emit: EmitFn<E>
// slots: Readonly<StaticSlots>
// TODO slots: Readonly<StaticSlots>
expose: (exposed?: Record<string, any>) => void
constructor(instance: VaporComponentInstance) {
@ -278,3 +280,47 @@ export class SetupContext<E = EmitsOptions> {
}
}
}
export function createComponentWithFallback(
comp: VaporComponent | string,
rawProps: RawProps | undefined,
// TODO slots: RawSlots | null
isSingleRoot?: boolean,
): HTMLElement | VaporComponentInstance {
if (!isString(comp)) {
return createComponent(comp, rawProps, isSingleRoot)
}
// eslint-disable-next-line no-restricted-globals
const el = document.createElement(comp)
if (rawProps) {
renderEffect(() => {
let classes: unknown[] | undefined
let styles: unknown[] | undefined
const resolved = resolveDynamicProps(rawProps)
for (const key in resolved) {
const value = resolved[key]
if (key === 'class') (classes ||= []).push(value)
else if (key === 'style') (styles ||= []).push(value)
else setDynamicProp(el, key, value)
}
if (classes) setClass(el, classes)
if (styles) setStyle(el, styles)
})
}
// TODO
// 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
}

View File

@ -231,14 +231,25 @@ export function setupPropsValidation(instance: VaporComponentInstance): void {
const rawProps = instance.rawProps
if (!rawProps) return
renderEffect(() => {
pushWarningContext(instance)
validateProps(
resolveDynamicProps(rawProps),
instance.props,
normalizePropsOptions(instance.type)[0]!,
)
popWarningContext()
}, true /* noLifecycle */)
}
export function resolveDynamicProps(props: RawProps): Record<string, unknown> {
const mergedRawProps: Record<string, any> = {}
for (const key in rawProps) {
for (const key in props) {
if (key !== '$') {
mergedRawProps[key] = rawProps[key]()
mergedRawProps[key] = props[key]()
}
}
if (rawProps.$) {
for (const source of rawProps.$) {
if (props.$) {
for (const source of props.$) {
const isDynamic = isFunction(source)
const resolved = isDynamic ? source() : source
for (const key in resolved) {
@ -248,12 +259,5 @@ export function setupPropsValidation(instance: VaporComponentInstance): void {
}
}
}
pushWarningContext(instance)
validateProps(
mergedRawProps,
instance.props,
normalizePropsOptions(instance.type)[0]!,
)
popWarningContext()
}, true /* noLifecycle */)
return mergedRawProps
}

View File

@ -1,4 +1,4 @@
export { createComponent } from './component'
export { createComponent, createComponentWithFallback } from './component'
export { renderEffect } from './renderEffect'
export { createVaporApp } from './apiCreateApp'
export { defineComponent } from './apiDefineComponent'
@ -19,3 +19,6 @@ export {
} from './dom/prop'
export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event'
export { setRef } from './dom/templateRef'
// re-exports
export { resolveComponent } from '@vue/runtime-dom'