|
|
@@ -48,14 +48,38 @@ const publicPropertiesMap = {
|
|
|
$options: 'type'
|
|
|
}
|
|
|
|
|
|
+const enum AccessTypes {
|
|
|
+ DATA,
|
|
|
+ CONTEXT,
|
|
|
+ PROPS
|
|
|
+}
|
|
|
+
|
|
|
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|
|
get(target: ComponentInternalInstance, key: string) {
|
|
|
- const { renderContext, data, props, propsProxy } = target
|
|
|
- if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
|
|
+ const { renderContext, data, props, propsProxy, accessCache } = target
|
|
|
+ // 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]
|
|
|
} else if (hasOwn(renderContext, key)) {
|
|
|
+ accessCache[key] = AccessTypes.CONTEXT
|
|
|
return renderContext[key]
|
|
|
} else if (hasOwn(props, key)) {
|
|
|
+ accessCache[key] = AccessTypes.PROPS
|
|
|
// return the value from propsProxy for ref unwrapping and readonly
|
|
|
return propsProxy![key]
|
|
|
} else if (key === '$el') {
|