wip: use proxy for static props too

This commit is contained in:
Evan You 2024-12-04 23:02:15 +08:00
parent 52fabd51af
commit 33d1b8bcec
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
2 changed files with 51 additions and 103 deletions

View File

@ -16,11 +16,11 @@ 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, hasOwn, isFunction } from '@vue/shared'
import {
type RawProps,
getDynamicPropsHandlers,
initStaticProps,
getPropsProxyHandlers,
normalizePropsOptions,
} from './componentProps'
import { setDynamicProp } from './dom/prop'
import { renderEffect } from './renderEffect'
@ -200,22 +200,30 @@ export class VaporComponentInstance implements GenericComponentInstance {
this.rawProps = rawProps
this.provides = this.refs = EMPTY_OBJ
this.emitted = this.ec = this.exposed = null
this.emitted = this.ec = this.exposed = this.propsDefaults = null
this.isMounted = this.isUnmounted = this.isDeactivated = false
// init props
this.propsDefaults = null
const target = rawProps || EMPTY_OBJ
const handlers = getPropsProxyHandlers(comp, this)
this.props = comp.props ? new Proxy(target, handlers[0]!) : {}
this.attrs = new Proxy(target, handlers[1])
// determine fallthrough
this.hasFallthrough = false
if (rawProps && rawProps.$) {
// has dynamic props, use proxy
const handlers = getDynamicPropsHandlers(comp, this)
this.props = comp.props ? new Proxy(rawProps, handlers[0]!) : EMPTY_OBJ
this.attrs = new Proxy(rawProps, handlers[1])
this.hasFallthrough = true
} else {
this.props = {}
this.attrs = {}
this.hasFallthrough = initStaticProps(comp, rawProps, this)
if (rawProps) {
if (rawProps.$) {
this.hasFallthrough = true
} else {
// check if rawProps contains any keys not declared
const propsOptions = normalizePropsOptions(comp)[0]!
for (const key in rawProps) {
if (!hasOwn(propsOptions, key)) {
this.hasFallthrough = true
break
}
}
}
}
// TODO validate props

View File

@ -1,4 +1,4 @@
import { EMPTY_ARR, NO, camelize, hasOwn, isFunction } from '@vue/shared'
import { EMPTY_ARR, NO, hasOwn, isFunction } from '@vue/shared'
import type { VaporComponent, VaporComponentInstance } from './component'
import {
type NormalizedPropsOptions,
@ -16,70 +16,6 @@ type DynamicPropsSource =
| (() => Record<string, unknown>)
| Record<string, () => unknown>
export function initStaticProps(
comp: VaporComponent,
rawProps: RawProps | undefined,
instance: VaporComponentInstance,
): boolean {
let hasAttrs = false
const { props, attrs } = instance
const [propsOptions, needCastKeys] = normalizePropsOptions(comp)
const emitsOptions = normalizeEmitsOptions(comp)
// for dev emit check
if (__DEV__) {
instance.propsOptions = normalizePropsOptions(comp)
instance.emitsOptions = emitsOptions
}
for (const key in rawProps) {
const normalizedKey = camelize(key)
const needCast = needCastKeys && needCastKeys.includes(normalizedKey)
const source = rawProps[key]
if (propsOptions && normalizedKey in propsOptions) {
Object.defineProperty(props, normalizedKey, {
enumerable: true,
get: needCast
? () =>
resolvePropValue(
propsOptions,
normalizedKey,
source(),
instance,
resolveDefault,
)
: source,
})
} else if (!isEmitListener(emitsOptions, key)) {
Object.defineProperty(attrs, key, {
enumerable: true,
get: source,
})
hasAttrs = true
}
}
for (const key in propsOptions) {
if (!(key in props)) {
props[key] = resolvePropValue(
propsOptions,
key,
undefined,
instance,
resolveDefault,
true,
)
}
}
return hasAttrs
}
function resolveDefault(
factory: (props: Record<string, any>) => unknown,
instance: VaporComponentInstance,
) {
return factory.call(null, instance.props)
}
// TODO optimization: maybe convert functions into computeds
export function resolveSource(
source: Record<string, any> | (() => Record<string, any>),
@ -89,41 +25,37 @@ export function resolveSource(
const passThrough = (val: any) => val
export function getDynamicPropsHandlers(
export function getPropsProxyHandlers(
comp: VaporComponent,
instance: VaporComponentInstance,
): [ProxyHandler<RawProps> | null, ProxyHandler<RawProps>] {
if (comp.__propsHandlers) {
return comp.__propsHandlers
}
let normalizedKeys: string[] | undefined
const propsOptions = normalizePropsOptions(comp)[0]
const emitsOptions = normalizeEmitsOptions(comp)
const isProp = propsOptions ? (key: string) => hasOwn(propsOptions, key) : NO
const castProp = propsOptions
? (key: string, value: any, isAbsent = false) =>
resolvePropValue(
propsOptions,
key as string,
value,
instance,
resolveDefault,
isAbsent,
)
: passThrough
const getProp = (target: RawProps, key: string, asProp: boolean) => {
if (key === '$') return
if (asProp) {
if (!isProp(key)) return
if (!isProp(key) || key === '$') return
} else if (isProp(key) || isEmitListener(emitsOptions, key)) {
return
}
const castProp = propsOptions
? (value: any, isAbsent = false) =>
asProp
? resolvePropValue(
propsOptions,
key as string,
value,
instance,
resolveDefault,
isAbsent,
)
: value
: passThrough
if (key in target) {
return castProp(target[key as string]())
return castProp(key, target[key as string]())
}
const dynamicSources = target.$
if (dynamicSources) {
@ -134,11 +66,11 @@ export function getDynamicPropsHandlers(
isDynamic = isFunction(source)
source = isDynamic ? (source as Function)() : source
if (hasOwn(source, key)) {
return castProp(isDynamic ? source[key] : source[key]())
return castProp(key, isDynamic ? source[key] : source[key]())
}
}
}
return castProp(undefined, true)
return castProp(key, undefined, true)
}
const propsHandlers = propsOptions
@ -154,8 +86,7 @@ export function getDynamicPropsHandlers(
}
}
},
ownKeys: () =>
normalizedKeys || (normalizedKeys = Object.keys(propsOptions)),
ownKeys: () => Object.keys(propsOptions),
set: NO,
deleteProperty: NO,
} satisfies ProxyHandler<RawProps>)
@ -207,7 +138,9 @@ export function getDynamicPropsHandlers(
return (comp.__propsHandlers = [propsHandlers, attrsHandlers])
}
function normalizePropsOptions(comp: VaporComponent): NormalizedPropsOptions {
export function normalizePropsOptions(
comp: VaporComponent,
): NormalizedPropsOptions {
const cached = comp.__propsOptions
if (cached) return cached
@ -220,3 +153,10 @@ function normalizePropsOptions(comp: VaporComponent): NormalizedPropsOptions {
return (comp.__propsOptions = [normalized, needCastKeys])
}
function resolveDefault(
factory: (props: Record<string, any>) => unknown,
instance: VaporComponentInstance,
) {
return factory.call(null, instance.props)
}