From: Evan You Date: Thu, 17 Oct 2019 02:13:52 +0000 (-0400) Subject: perf(core): cache property access types on renderProxy X-Git-Tag: v3.0.0-alpha.0~387 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4771319a1544afa46920c30e33c02d4fe5fd9561;p=thirdparty%2Fvuejs%2Fcore.git perf(core): cache property access types on renderProxy --- diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index c0583746b8..39abd4f127 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -82,6 +82,7 @@ export interface ComponentInternalInstance { render: RenderFunction | null effects: ReactiveEffect[] | null provides: Data + accessCache: Data components: Record directives: Record @@ -146,6 +147,7 @@ export function createComponentInstance( setupContext: null, effects: null, provides: parent ? parent.provides : Object.create(appContext.provides), + accessCache: null!, // setup context properties 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 instance.renderProxy = new Proxy(instance, PublicInstanceProxyHandlers) // 2. create props proxy diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index 3d31bc218e..bc1a1f5466 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -48,14 +48,38 @@ const publicPropertiesMap = { $options: 'type' } +const enum AccessTypes { + DATA, + CONTEXT, + PROPS +} + export const PublicInstanceProxyHandlers: ProxyHandler = { 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') {