]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
perf(core): cache property access types on renderProxy
authorEvan You <yyx990803@gmail.com>
Thu, 17 Oct 2019 02:13:52 +0000 (22:13 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 17 Oct 2019 19:02:15 +0000 (15:02 -0400)
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentProxy.ts

index c0583746b8fa4c1b46a08630c0c8c7e40571c2a7..39abd4f127b5e7b02f2b901e3b7b25f0e4504905 100644 (file)
@@ -82,6 +82,7 @@ export interface ComponentInternalInstance {
   render: RenderFunction | null
   effects: ReactiveEffect[] | null
   provides: Data
+  accessCache: Data
 
   components: Record<string, Component>
   directives: Record<string, Directive>
@@ -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
index 3d31bc218efcd991082e481ae14a7495ae284fa7..bc1a1f54664b2cc559ed79a827f53b3b332d5453 100644 (file)
@@ -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') {