perf(core): cache property access types on renderProxy
This commit is contained in:
parent
cdee65aa1b
commit
4771319a15
@ -82,6 +82,7 @@ export interface ComponentInternalInstance {
|
|||||||
render: RenderFunction | null
|
render: RenderFunction | null
|
||||||
effects: ReactiveEffect[] | null
|
effects: ReactiveEffect[] | null
|
||||||
provides: Data
|
provides: Data
|
||||||
|
accessCache: Data
|
||||||
|
|
||||||
components: Record<string, Component>
|
components: Record<string, Component>
|
||||||
directives: Record<string, Directive>
|
directives: Record<string, Directive>
|
||||||
@ -146,6 +147,7 @@ export function createComponentInstance(
|
|||||||
setupContext: null,
|
setupContext: null,
|
||||||
effects: null,
|
effects: null,
|
||||||
provides: parent ? parent.provides : Object.create(appContext.provides),
|
provides: parent ? parent.provides : Object.create(appContext.provides),
|
||||||
|
accessCache: null!,
|
||||||
|
|
||||||
// setup context properties
|
// setup context properties
|
||||||
renderContext: EMPTY_OBJ,
|
renderContext: EMPTY_OBJ,
|
||||||
@ -254,7 +256,8 @@ export function setupStatefulComponent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 0. create render proxy property access cache
|
||||||
|
instance.accessCache = Object.create(null)
|
||||||
// 1. create render proxy
|
// 1. create render proxy
|
||||||
instance.renderProxy = new Proxy(instance, PublicInstanceProxyHandlers)
|
instance.renderProxy = new Proxy(instance, PublicInstanceProxyHandlers)
|
||||||
// 2. create props proxy
|
// 2. create props proxy
|
||||||
|
@ -48,14 +48,38 @@ const publicPropertiesMap = {
|
|||||||
$options: 'type'
|
$options: 'type'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const enum AccessTypes {
|
||||||
|
DATA,
|
||||||
|
CONTEXT,
|
||||||
|
PROPS
|
||||||
|
}
|
||||||
|
|
||||||
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
||||||
get(target: ComponentInternalInstance, key: string) {
|
get(target: ComponentInternalInstance, key: string) {
|
||||||
const { renderContext, data, props, propsProxy } = target
|
const { renderContext, data, props, propsProxy, accessCache } = target
|
||||||
if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
// This getter gets called for every property access on the render context
|
||||||
|
// during render and is a major hotspot. The most expensive part of this
|
||||||
|
// is the multiple hasOwn() calls. It's much faster to do a simple property
|
||||||
|
// access on a plain object, so we use an accessCache object (with null
|
||||||
|
// prototype) to memoize what access type a key corresponds to.
|
||||||
|
const n = accessCache[key]
|
||||||
|
if (n !== undefined) {
|
||||||
|
switch (n) {
|
||||||
|
case AccessTypes.DATA:
|
||||||
|
return data[key]
|
||||||
|
case AccessTypes.CONTEXT:
|
||||||
|
return renderContext[key]
|
||||||
|
case AccessTypes.PROPS:
|
||||||
|
return propsProxy![key]
|
||||||
|
}
|
||||||
|
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
||||||
|
accessCache[key] = AccessTypes.DATA
|
||||||
return data[key]
|
return data[key]
|
||||||
} else if (hasOwn(renderContext, key)) {
|
} else if (hasOwn(renderContext, key)) {
|
||||||
|
accessCache[key] = AccessTypes.CONTEXT
|
||||||
return renderContext[key]
|
return renderContext[key]
|
||||||
} else if (hasOwn(props, key)) {
|
} else if (hasOwn(props, key)) {
|
||||||
|
accessCache[key] = AccessTypes.PROPS
|
||||||
// return the value from propsProxy for ref unwrapping and readonly
|
// return the value from propsProxy for ref unwrapping and readonly
|
||||||
return propsProxy![key]
|
return propsProxy![key]
|
||||||
} else if (key === '$el') {
|
} else if (key === '$el') {
|
||||||
|
Loading…
Reference in New Issue
Block a user