refactor(reactivity): encapsulate reactive handlers in class (#8586)

reactive obj create has a huge positive impact (200% - 700%)
get/set reactive obj props has a small negative impact (1% - 5%)
This commit is contained in:
Waleed Khaled 2023-08-22 11:50:27 +03:00 committed by GitHub
parent e7d5a41758
commit 02c6924bcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 55 additions and 58 deletions

View File

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