wip: get instance from rawProps to fix proxy handler caching

This commit is contained in:
Evan You 2024-12-06 21:08:24 +08:00
parent f6f3f14a3e
commit 238d1817cc
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
2 changed files with 65 additions and 39 deletions

View File

@ -32,6 +32,11 @@ import { renderEffect } from './renderEffect'
import { emit, normalizeEmitsOptions } from './componentEmits' import { emit, normalizeEmitsOptions } from './componentEmits'
import { setStyle } from './dom/style' import { setStyle } from './dom/style'
import { setClass, setDynamicProp } from './dom/prop' import { setClass, setDynamicProp } from './dom/prop'
import {
type RawSlots,
type Slot,
getSlotsProxyHandlers,
} from './componentSlots'
export { currentInstance } from '@vue/runtime-dom' export { currentInstance } from '@vue/runtime-dom'
@ -170,9 +175,10 @@ export class VaporComponentInstance implements GenericComponentInstance {
block: Block block: Block
scope: EffectScope scope: EffectScope
rawProps: RawProps | undefined rawProps: RawProps
props: Record<string, any> props: Record<string, any>
attrs: Record<string, any> attrs: Record<string, any>
slots: Record<string, Slot>
exposed: Record<string, any> | null exposed: Record<string, any> | null
emitted: Record<string, boolean> | null emitted: Record<string, boolean> | null
@ -215,7 +221,7 @@ export class VaporComponentInstance implements GenericComponentInstance {
propsOptions?: NormalizedPropsOptions propsOptions?: NormalizedPropsOptions
emitsOptions?: ObjectEmitsOptions | null emitsOptions?: ObjectEmitsOptions | null
constructor(comp: VaporComponent, rawProps?: RawProps) { constructor(comp: VaporComponent, rawProps?: RawProps, rawSlots?: RawSlots) {
this.vapor = true this.vapor = true
this.uid = nextUid() this.uid = nextUid()
this.type = comp this.type = comp
@ -240,12 +246,20 @@ export class VaporComponentInstance implements GenericComponentInstance {
false false
// init props // init props
const target = rawProps || EMPTY_OBJ this.rawProps = rawProps || EMPTY_OBJ
const handlers = getPropsProxyHandlers(comp, this)
this.rawProps = rawProps
this.props = comp.props ? new Proxy(target, handlers[0]!) : {}
this.attrs = new Proxy(target, handlers[1])
this.hasFallthrough = hasFallthroughAttrs(comp, rawProps) this.hasFallthrough = hasFallthroughAttrs(comp, rawProps)
if (rawProps || comp.props) {
const [propsHandlers, attrsHandlers] = getPropsProxyHandlers(comp)
this.props = comp.props ? new Proxy(this, propsHandlers!) : {}
this.attrs = new Proxy(this, attrsHandlers)
} else {
this.props = this.attrs = EMPTY_OBJ
}
// init slots
this.slots = rawSlots
? new Proxy(rawSlots, getSlotsProxyHandlers(comp))
: EMPTY_OBJ
if (__DEV__) { if (__DEV__) {
// validate props // validate props
@ -281,6 +295,11 @@ export class SetupContext<E = EmitsOptions> {
} }
} }
/**
* Used when a component cannot be resolved at compile time
* and needs rely on runtime resolution - where it might fallback to a plain
* element if the resolution fails.
*/
export function createComponentWithFallback( export function createComponentWithFallback(
comp: VaporComponent | string, comp: VaporComponent | string,
rawProps: RawProps | undefined, rawProps: RawProps | undefined,

View File

@ -13,6 +13,7 @@ import { normalizeEmitsOptions } from './componentEmits'
import { renderEffect } from './renderEffect' import { renderEffect } from './renderEffect'
export type RawProps = Record<string, () => unknown> & { export type RawProps = Record<string, () => unknown> & {
// generated by compiler for :[key]="x" or v-bind="x"
$?: DynamicPropsSource[] $?: DynamicPropsSource[]
} }
@ -27,12 +28,12 @@ export function resolveSource(
return isFunction(source) ? source() : source return isFunction(source) ? source() : source
} }
const passThrough = (val: any) => val
export function getPropsProxyHandlers( export function getPropsProxyHandlers(
comp: VaporComponent, comp: VaporComponent,
instance: VaporComponentInstance, ): [
): [ProxyHandler<RawProps> | null, ProxyHandler<RawProps>] { ProxyHandler<VaporComponentInstance> | null,
ProxyHandler<VaporComponentInstance>,
] {
if (comp.__propsHandlers) { if (comp.__propsHandlers) {
return comp.__propsHandlers return comp.__propsHandlers
} }
@ -44,23 +45,12 @@ export function getPropsProxyHandlers(
key !== '$' && !isProp(key) && !isEmitListener(emitsOptions, key) key !== '$' && !isProp(key) && !isEmitListener(emitsOptions, key)
: YES : YES
const castProp = propsOptions const getProp = (instance: VaporComponentInstance, key: string) => {
? (value: any, key: string, isAbsent = false) =>
resolvePropValue(
propsOptions,
key as string,
value,
instance,
resolveDefault,
isAbsent,
)
: passThrough
const getProp = (target: RawProps, key: string) => {
if (key === '$' || !isProp(key)) { if (key === '$' || !isProp(key)) {
return return
} }
const dynamicSources = target.$ const rawProps = instance.rawProps
const dynamicSources = rawProps.$
if (dynamicSources) { if (dynamicSources) {
let i = dynamicSources.length let i = dynamicSources.length
let source, isDynamic, rawKey let source, isDynamic, rawKey
@ -70,17 +60,35 @@ export function getPropsProxyHandlers(
source = isDynamic ? (source as Function)() : source source = isDynamic ? (source as Function)() : source
for (rawKey in source) { for (rawKey in source) {
if (camelize(rawKey) === key) { if (camelize(rawKey) === key) {
return castProp(isDynamic ? source[rawKey] : source[rawKey](), key) return resolvePropValue(
propsOptions!,
key,
isDynamic ? source[rawKey] : source[rawKey](),
instance,
resolveDefault,
)
} }
} }
} }
} }
for (const rawKey in target) { for (const rawKey in rawProps) {
if (camelize(rawKey) === key) { if (camelize(rawKey) === key) {
return castProp(target[rawKey](), key) return resolvePropValue(
propsOptions!,
key,
rawProps[rawKey](),
instance,
resolveDefault,
)
} }
} }
return castProp(undefined, key, true) return resolvePropValue(
propsOptions!,
key,
undefined,
instance,
resolveDefault,
)
} }
const propsHandlers = propsOptions const propsHandlers = propsOptions
@ -99,7 +107,7 @@ export function getPropsProxyHandlers(
ownKeys: () => Object.keys(propsOptions), ownKeys: () => Object.keys(propsOptions),
set: NO, set: NO,
deleteProperty: NO, deleteProperty: NO,
} satisfies ProxyHandler<RawProps>) } satisfies ProxyHandler<VaporComponentInstance>)
: null : null
const getAttr = (target: RawProps, key: string) => { const getAttr = (target: RawProps, key: string) => {
@ -142,25 +150,24 @@ export function getPropsProxyHandlers(
} }
const attrsHandlers = { const attrsHandlers = {
get: (target, key: string) => { get: (target, key: string) => getAttr(target.rawProps, key),
return getAttr(target, key) has: (target, key: string) => hasAttr(target.rawProps, key),
},
has: hasAttr,
getOwnPropertyDescriptor(target, key: string) { getOwnPropertyDescriptor(target, key: string) {
if (hasAttr(target, key)) { if (hasAttr(target.rawProps, key)) {
return { return {
configurable: true, configurable: true,
enumerable: true, enumerable: true,
get: () => getAttr(target, key), get: () => getAttr(target.rawProps, key),
} }
} }
}, },
ownKeys(target) { ownKeys(target) {
const rawProps = target.rawProps
const keys: string[] = [] const keys: string[] = []
for (const key in target) { for (const key in rawProps) {
if (isAttr(key)) keys.push(key) if (isAttr(key)) keys.push(key)
} }
const dynamicSources = target.$ const dynamicSources = rawProps.$
if (dynamicSources) { if (dynamicSources) {
let i = dynamicSources.length let i = dynamicSources.length
let source let source
@ -175,7 +182,7 @@ export function getPropsProxyHandlers(
}, },
set: NO, set: NO,
deleteProperty: NO, deleteProperty: NO,
} satisfies ProxyHandler<RawProps> } satisfies ProxyHandler<VaporComponentInstance>
return (comp.__propsHandlers = [propsHandlers, attrsHandlers]) return (comp.__propsHandlers = [propsHandlers, attrsHandlers])
} }