]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(reactivity): encapsulate reactive handlers in class (#8586)
authorWaleed Khaled <waleed1kh@outlook.com>
Tue, 22 Aug 2023 08:50:27 +0000 (11:50 +0300)
committerGitHub <noreply@github.com>
Tue, 22 Aug 2023 08:50:27 +0000 (16:50 +0800)
reactive obj create has a huge positive impact (200% - 700%)
get/set reactive obj props has a small negative impact (1% - 5%)

packages/reactivity/src/baseHandlers.ts

index 45ecfa6d38ab820a5b8788f03dc602b03d7159c9..259b44a1edcc17cc6ae04403c91a628a168bbfbe 100644 (file)
@@ -26,7 +26,6 @@ import {
   hasChanged,
   isArray,
   isIntegerKey,
-  extend,
   makeMap
 } from '@vue/shared'
 import { isRef } from './ref'
@@ -45,11 +44,6 @@ const builtInSymbols = new Set(
     .filter(isSymbol)
 )
 
-const get = /*#__PURE__*/ createGetter()
-const shallowGet = /*#__PURE__*/ createGetter(false, true)
-const readonlyGet = /*#__PURE__*/ createGetter(true)
-const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
-
 const arrayInstrumentations = /*#__PURE__*/ createArrayInstrumentations()
 
 function createArrayInstrumentations() {
@@ -91,8 +85,15 @@ function hasOwnProperty(this: object, key: string) {
   return obj.hasOwnProperty(key)
 }
 
-function createGetter(isReadonly = false, shallow = false) {
-  return function get(target: Target, key: string | symbol, receiver: object) {
+class BaseReactiveHandler implements ProxyHandler<Target> {
+  constructor(
+    protected readonly _isReadonly = false,
+    protected readonly _shallow = false
+  ) {}
+
+  get(target: Target, key: string | symbol, receiver: object) {
+    const isReadonly = this._isReadonly,
+      shallow = this._shallow
     if (key === ReactiveFlags.IS_REACTIVE) {
       return !isReadonly
     } else if (key === ReactiveFlags.IS_READONLY) {
@@ -155,11 +156,12 @@ function createGetter(isReadonly = false, shallow = false) {
   }
 }
 
-const set = /*#__PURE__*/ createSetter()
-const shallowSet = /*#__PURE__*/ createSetter(true)
+class MutableReactiveHandler extends BaseReactiveHandler {
+  constructor(shallow = false) {
+    super(false, shallow)
+  }
 
-function createSetter(shallow = false) {
-  return function set(
+  set(
     target: object,
     key: string | symbol,
     value: unknown,
@@ -169,7 +171,7 @@ function createSetter(shallow = false) {
     if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) {
       return false
     }
-    if (!shallow) {
+    if (!this._shallow) {
       if (!isShallow(value) && !isReadonly(value)) {
         oldValue = toRaw(oldValue)
         value = toRaw(value)
@@ -197,42 +199,40 @@ function createSetter(shallow = false) {
     }
     return result
   }
-}
 
-function deleteProperty(target: object, key: string | symbol): boolean {
-  const hadKey = hasOwn(target, key)
-  const oldValue = (target as any)[key]
-  const result = Reflect.deleteProperty(target, key)
-  if (result && hadKey) {
-    trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
+  deleteProperty(target: object, key: string | symbol): boolean {
+    const hadKey = hasOwn(target, key)
+    const oldValue = (target as any)[key]
+    const result = Reflect.deleteProperty(target, key)
+    if (result && hadKey) {
+      trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
+    }
+    return result
   }
-  return result
-}
 
-function has(target: object, key: string | symbol): boolean {
-  const result = Reflect.has(target, key)
-  if (!isSymbol(key) || !builtInSymbols.has(key)) {
-    track(target, TrackOpTypes.HAS, key)
+  has(target: object, key: string | symbol): boolean {
+    const result = Reflect.has(target, key)
+    if (!isSymbol(key) || !builtInSymbols.has(key)) {
+      track(target, TrackOpTypes.HAS, key)
+    }
+    return result
+  }
+  ownKeys(target: object): (string | symbol)[] {
+    track(
+      target,
+      TrackOpTypes.ITERATE,
+      isArray(target) ? 'length' : ITERATE_KEY
+    )
+    return Reflect.ownKeys(target)
   }
-  return result
-}
-
-function ownKeys(target: object): (string | symbol)[] {
-  track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY)
-  return Reflect.ownKeys(target)
 }
 
-export const mutableHandlers: ProxyHandler<object> = {
-  get,
-  set,
-  deleteProperty,
-  has,
-  ownKeys
-}
+class ReadonlyReactiveHandler extends BaseReactiveHandler {
+  constructor(shallow = false) {
+    super(true, shallow)
+  }
 
-export const readonlyHandlers: ProxyHandler<object> = {
-  get: readonlyGet,
-  set(target, key) {
+  set(target: object, key: string | symbol) {
     if (__DEV__) {
       warn(
         `Set operation on key "${String(key)}" failed: target is readonly.`,
@@ -240,8 +240,9 @@ export const readonlyHandlers: ProxyHandler<object> = {
       )
     }
     return true
-  },
-  deleteProperty(target, key) {
+  }
+
+  deleteProperty(target: object, key: string | symbol) {
     if (__DEV__) {
       warn(
         `Delete operation on key "${String(key)}" failed: target is readonly.`,
@@ -252,22 +253,18 @@ export const readonlyHandlers: ProxyHandler<object> = {
   }
 }
 
-export const shallowReactiveHandlers = /*#__PURE__*/ extend(
-  {},
-  mutableHandlers,
-  {
-    get: shallowGet,
-    set: shallowSet
-  }
+export const mutableHandlers: ProxyHandler<object> =
+  /*#__PURE__*/ new MutableReactiveHandler()
+
+export const readonlyHandlers: ProxyHandler<object> =
+  /*#__PURE__*/ new ReadonlyReactiveHandler()
+
+export const shallowReactiveHandlers = /*#__PURE__*/ new MutableReactiveHandler(
+  true
 )
 
 // Props handlers are special in the sense that it should not unwrap top-level
 // refs (in order to allow refs to be explicitly passed down), but should
 // retain the reactivity of the normal readonly object.
-export const shallowReadonlyHandlers = /*#__PURE__*/ extend(
-  {},
-  readonlyHandlers,
-  {
-    get: shallowReadonlyGet
-  }
-)
+export const shallowReadonlyHandlers =
+  /*#__PURE__*/ new ReadonlyReactiveHandler(true)