mirror of https://github.com/vuejs/core.git
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:
parent
e7d5a41758
commit
02c6924bcd
|
@ -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
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in New Issue