From 6d667bfbfb2c217d6565f8ae993926d488424ccc Mon Sep 17 00:00:00 2001 From: Waleed Khaled Date: Thu, 15 Jun 2023 20:57:03 +0400 Subject: [PATCH 1/2] refactor(reactivity): encapsulate reactive handlers in class --- packages/reactivity/src/baseHandlers.ts | 131 ++++++++++++------------ 1 file changed, 63 insertions(+), 68 deletions(-) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index 45ecfa6d38a..77ef721bb24 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -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,22 +85,27 @@ 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 { + constructor( + protected readonly _isReadonly = false, + protected readonly _shallow = false + ) {} + + get(target: Target, key: string | symbol, receiver: object) { if (key === ReactiveFlags.IS_REACTIVE) { - return !isReadonly + return !this._isReadonly } else if (key === ReactiveFlags.IS_READONLY) { - return isReadonly + return this._isReadonly } else if (key === ReactiveFlags.IS_SHALLOW) { - return shallow + return this._shallow } else if ( key === ReactiveFlags.RAW && receiver === - (isReadonly - ? shallow + (this._isReadonly + ? this._shallow ? shallowReadonlyMap : readonlyMap - : shallow + : this._shallow ? shallowReactiveMap : reactiveMap ).get(target) @@ -116,7 +115,7 @@ function createGetter(isReadonly = false, shallow = false) { const targetIsArray = isArray(target) - if (!isReadonly) { + if (!this._isReadonly) { if (targetIsArray && hasOwn(arrayInstrumentations, key)) { return Reflect.get(arrayInstrumentations, key, receiver) } @@ -131,11 +130,11 @@ function createGetter(isReadonly = false, shallow = false) { return res } - if (!isReadonly) { + if (!this._isReadonly) { track(target, TrackOpTypes.GET, key) } - if (shallow) { + if (this._shallow) { return res } @@ -148,18 +147,19 @@ function createGetter(isReadonly = false, shallow = false) { // Convert returned value into a proxy as well. we do the isObject check // here to avoid invalid value warning. Also need to lazy access readonly // and reactive here to avoid circular dependency. - return isReadonly ? readonly(res) : reactive(res) + return this._isReadonly ? readonly(res) : reactive(res) } return res } } -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 +169,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 +197,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 = { - get, - set, - deleteProperty, - has, - ownKeys -} +class ReadonlyReactiveHandler extends BaseReactiveHandler { + constructor(shallow = false) { + super(true, shallow) + } -export const readonlyHandlers: ProxyHandler = { - 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 +238,9 @@ export const readonlyHandlers: ProxyHandler = { ) } 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 +251,18 @@ export const readonlyHandlers: ProxyHandler = { } } -export const shallowReactiveHandlers = /*#__PURE__*/ extend( - {}, - mutableHandlers, - { - get: shallowGet, - set: shallowSet - } +export const mutableHandlers: ProxyHandler = + /*#__PURE__*/ new MutableReactiveHandler() + +export const readonlyHandlers: ProxyHandler = + /*#__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) From 127e0783d219847f18095ee9bfb977ff5f764a1c Mon Sep 17 00:00:00 2001 From: Waleed Khaled Date: Tue, 15 Aug 2023 14:49:46 +0400 Subject: [PATCH 2/2] chore: access `BaseReactiveHandler` props as const --- packages/reactivity/src/baseHandlers.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/reactivity/src/baseHandlers.ts b/packages/reactivity/src/baseHandlers.ts index 77ef721bb24..259b44a1edc 100644 --- a/packages/reactivity/src/baseHandlers.ts +++ b/packages/reactivity/src/baseHandlers.ts @@ -92,20 +92,22 @@ class BaseReactiveHandler implements ProxyHandler { ) {} get(target: Target, key: string | symbol, receiver: object) { + const isReadonly = this._isReadonly, + shallow = this._shallow if (key === ReactiveFlags.IS_REACTIVE) { - return !this._isReadonly + return !isReadonly } else if (key === ReactiveFlags.IS_READONLY) { - return this._isReadonly + return isReadonly } else if (key === ReactiveFlags.IS_SHALLOW) { - return this._shallow + return shallow } else if ( key === ReactiveFlags.RAW && receiver === - (this._isReadonly - ? this._shallow + (isReadonly + ? shallow ? shallowReadonlyMap : readonlyMap - : this._shallow + : shallow ? shallowReactiveMap : reactiveMap ).get(target) @@ -115,7 +117,7 @@ class BaseReactiveHandler implements ProxyHandler { const targetIsArray = isArray(target) - if (!this._isReadonly) { + if (!isReadonly) { if (targetIsArray && hasOwn(arrayInstrumentations, key)) { return Reflect.get(arrayInstrumentations, key, receiver) } @@ -130,11 +132,11 @@ class BaseReactiveHandler implements ProxyHandler { return res } - if (!this._isReadonly) { + if (!isReadonly) { track(target, TrackOpTypes.GET, key) } - if (this._shallow) { + if (shallow) { return res } @@ -147,7 +149,7 @@ class BaseReactiveHandler implements ProxyHandler { // Convert returned value into a proxy as well. we do the isObject check // here to avoid invalid value warning. Also need to lazy access readonly // and reactive here to avoid circular dependency. - return this._isReadonly ? readonly(res) : reactive(res) + return isReadonly ? readonly(res) : reactive(res) } return res