From 5196f4526aab69ed9ae332aa64f6b4b7fdc400fb Mon Sep 17 00:00:00 2001 From: Janry Date: Fri, 30 Sep 2022 11:47:14 +0800 Subject: [PATCH] perf(reactive): improve reactive performance (#3430) --- packages/reactive/src/annotations/computed.ts | 11 +++++------ packages/reactive/src/annotations/ref.ts | 5 ++--- packages/reactive/src/environment.ts | 4 +++- packages/reactive/src/externals.ts | 16 ++++++++++------ packages/reactive/src/internals.ts | 16 ++++++++-------- packages/reactive/src/model.ts | 5 ++--- packages/reactive/src/observable.ts | 4 ++-- packages/reactive/src/observe.ts | 7 ++++--- packages/reactive/src/reaction.ts | 5 +++-- packages/reactive/src/tree.ts | 14 +++++++++++--- 10 files changed, 50 insertions(+), 37 deletions(-) diff --git a/packages/reactive/src/annotations/computed.ts b/packages/reactive/src/annotations/computed.ts index c08072c8909..9e35ddd545e 100644 --- a/packages/reactive/src/annotations/computed.ts +++ b/packages/reactive/src/annotations/computed.ts @@ -1,4 +1,4 @@ -import { ProxyRaw, RawProxy, ReactionStack } from '../environment' +import { ObModelSymbol, ReactionStack } from '../environment' import { createAnnotation } from '../internals' import { buildDataTree } from '../tree' import { isFn } from '../checkers' @@ -25,7 +25,7 @@ const getDescriptor = Object.getOwnPropertyDescriptor const getProto = Object.getPrototypeOf -const ClassDescriptorMap = new WeakMap() +const ClassDescriptorSymbol = Symbol('ClassDescriptorSymbol') function getPropertyDescriptor(obj: any, key: PropertyKey) { if (!obj) return @@ -36,11 +36,11 @@ function getPropertyDescriptorCache(obj: any, key: PropertyKey) { const constructor = obj.constructor if (constructor === Object || constructor === Array) return getPropertyDescriptor(obj, key) - const cache = ClassDescriptorMap.get(constructor) || {} + const cache = constructor[ClassDescriptorSymbol] || {} const descriptor = cache[key] if (descriptor) return descriptor const newDesc = getPropertyDescriptor(obj, key) - ClassDescriptorMap.set(constructor, cache) + constructor[ClassDescriptorSymbol] = cache cache[key] = newDesc return newDesc } @@ -148,8 +148,7 @@ export const computed: IComputed = createAnnotation( get, }) buildDataTree(target, key, store) - ProxyRaw.set(proxy, store) - RawProxy.set(store, proxy) + proxy[ObModelSymbol] = store } return proxy } diff --git a/packages/reactive/src/annotations/ref.ts b/packages/reactive/src/annotations/ref.ts index 008ea4beae1..8fe8bfae596 100644 --- a/packages/reactive/src/annotations/ref.ts +++ b/packages/reactive/src/annotations/ref.ts @@ -1,4 +1,4 @@ -import { ProxyRaw, RawProxy } from '../environment' +import { ObModelSymbol } from '../environment' import { createAnnotation } from '../internals' import { buildDataTree } from '../tree' import { @@ -55,8 +55,7 @@ export const ref: IRef = createAnnotation(({ target, key, value }) => { get, }) buildDataTree(target, key, store) - ProxyRaw.set(proxy, store) - RawProxy.set(store, proxy) + proxy[ObModelSymbol] = store } return proxy }) diff --git a/packages/reactive/src/environment.ts b/packages/reactive/src/environment.ts index 5c5f61efc6b..52b769fb0ed 100644 --- a/packages/reactive/src/environment.ts +++ b/packages/reactive/src/environment.ts @@ -16,5 +16,7 @@ export const DependencyCollected = { value: false } export const PendingReactions = new ArraySet() export const PendingScopeReactions = new ArraySet() export const BatchEndpoints = new ArraySet<() => void>() -export const MakeObservableSymbol = Symbol('MakeObservableSymbol') export const ObserverListeners = new ArraySet() +export const MakeObModelSymbol = Symbol('MakeObModelSymbol') +export const ObModelSymbol = Symbol('ObModelSymbol') +export const ObModelNodeSymbol = Symbol('ObModelNodeSymbol') diff --git a/packages/reactive/src/externals.ts b/packages/reactive/src/externals.ts index c0709d774c9..c2dd0ad8ef4 100644 --- a/packages/reactive/src/externals.ts +++ b/packages/reactive/src/externals.ts @@ -10,8 +10,9 @@ import { } from './checkers' import { ProxyRaw, - MakeObservableSymbol, + MakeObModelSymbol, DependencyCollected, + ObModelSymbol, } from './environment' import { getDataNode } from './tree' import { Annotation } from './types' @@ -21,11 +22,11 @@ const OBSERVABLE_TYPE = Symbol('OBSERVABLE_TYPE') const hasOwnProperty = Object.prototype.hasOwnProperty export const isObservable = (target: any) => { - return ProxyRaw.has(target) + return ProxyRaw.has(target) || !!target?.[ObModelSymbol] } export const isAnnotation = (target: any): target is Annotation => { - return target && !!target[MakeObservableSymbol] + return target && !!target[MakeObModelSymbol] } export const isSupportObservable = (target: any) => { @@ -80,7 +81,10 @@ export const markObservable = (target: T): T => { return target } -export const raw = (target: T): T => ProxyRaw.get(target as any) +export const raw = (target: T): T => { + if (target?.[ObModelSymbol]) return target[ObModelSymbol] + return ProxyRaw.get(target as any) || target +} export const toJS = (values: T): T => { const visited = new WeakSet() @@ -119,8 +123,8 @@ export const toJS = (values: T): T => { } export const contains = (target: any, property: any) => { - const targetRaw = ProxyRaw.get(target) || target - const propertyRaw = ProxyRaw.get(property) || property + const targetRaw = raw(target) + const propertyRaw = raw(property) if (targetRaw === propertyRaw) return true const targetNode = getDataNode(targetRaw) const propertyNode = getDataNode(propertyRaw) diff --git a/packages/reactive/src/internals.ts b/packages/reactive/src/internals.ts index 6926189724b..775c491ba85 100644 --- a/packages/reactive/src/internals.ts +++ b/packages/reactive/src/internals.ts @@ -2,7 +2,7 @@ import { isFn, isCollectionType, isNormalType } from './checkers' import { RawProxy, ProxyRaw, - MakeObservableSymbol, + MakeObModelSymbol, RawShallowProxy, } from './environment' import { baseHandlers, collectionHandlers } from './handlers' @@ -75,17 +75,17 @@ export const createAnnotation = any>( return maker({ value: target }) } if (isFn(maker)) { - annotation[MakeObservableSymbol] = maker + annotation[MakeObModelSymbol] = maker } return annotation } export const getObservableMaker = (target: any) => { - if (target[MakeObservableSymbol]) { - if (!target[MakeObservableSymbol][MakeObservableSymbol]) { - return target[MakeObservableSymbol] + if (target[MakeObModelSymbol]) { + if (!target[MakeObModelSymbol][MakeObModelSymbol]) { + return target[MakeObModelSymbol] } - return getObservableMaker(target[MakeObservableSymbol]) + return getObservableMaker(target[MakeObModelSymbol]) } } @@ -132,7 +132,7 @@ export const createBoundaryAnnotation = ( target[key] = boundary.bound(target[key], target) return target }) - boundary[MakeObservableSymbol] = annotation - boundary.bound[MakeObservableSymbol] = annotation + boundary[MakeObModelSymbol] = annotation + boundary.bound[MakeObModelSymbol] = annotation return boundary } diff --git a/packages/reactive/src/model.ts b/packages/reactive/src/model.ts index d9b9a02bdc8..f46f44fb900 100644 --- a/packages/reactive/src/model.ts +++ b/packages/reactive/src/model.ts @@ -5,7 +5,7 @@ import { getObservableMaker } from './internals' import { isObservable, isAnnotation, isSupportObservable } from './externals' import { Annotations } from './types' import { action } from './action' -import { ProxyRaw, RawProxy } from './environment' +import { ObModelSymbol } from './environment' export function define( target: Target, @@ -13,9 +13,8 @@ export function define( ): Target { if (isObservable(target)) return target if (!isSupportObservable(target)) return target + target[ObModelSymbol] = target buildDataTree(undefined, undefined, target) - ProxyRaw.set(target, target) - RawProxy.set(target, target) for (const key in annotations) { const annotation = annotations[key] if (isAnnotation(annotation)) { diff --git a/packages/reactive/src/observable.ts b/packages/reactive/src/observable.ts index 11bad7fa037..6884b65de91 100644 --- a/packages/reactive/src/observable.ts +++ b/packages/reactive/src/observable.ts @@ -1,5 +1,5 @@ import * as annotations from './annotations' -import { MakeObservableSymbol } from './environment' +import { MakeObModelSymbol } from './environment' import { createObservable } from './internals' export function observable(target: T): T { @@ -11,4 +11,4 @@ observable.ref = annotations.ref observable.deep = annotations.observable observable.shallow = annotations.shallow observable.computed = annotations.computed -observable[MakeObservableSymbol] = annotations.observable +observable[MakeObModelSymbol] = annotations.observable diff --git a/packages/reactive/src/observe.ts b/packages/reactive/src/observe.ts index d7ba0f8542b..051713ac90d 100644 --- a/packages/reactive/src/observe.ts +++ b/packages/reactive/src/observe.ts @@ -1,5 +1,6 @@ import { IOperation } from './types' -import { ProxyRaw, ObserverListeners } from './environment' +import { ObserverListeners } from './environment' +import { raw as getRaw } from './externals' import { isFn } from './checkers' import { DataChange, getDataNode } from './tree' @@ -9,11 +10,11 @@ export const observe = ( deep = true ) => { const addListener = (target: any) => { - const raw = ProxyRaw.get(target) || target + const raw = getRaw(target) const node = getDataNode(raw) const listener = (operation: IOperation) => { - const targetRaw = ProxyRaw.get(operation.target) || operation.target + const targetRaw = getRaw(operation.target) const targetNode = getDataNode(targetRaw) if (deep) { if (node.contains(targetNode)) { diff --git a/packages/reactive/src/reaction.ts b/packages/reactive/src/reaction.ts index 7260fc3111b..38444eae094 100644 --- a/packages/reactive/src/reaction.ts +++ b/packages/reactive/src/reaction.ts @@ -100,8 +100,9 @@ export const bindTargetKeyWithCurrentReaction = (operation: IOperation) => { if (type === 'iterate') { key = ITERATION_KEY } - - const current = ReactionStack[ReactionStack.length - 1] + const reactionLen = ReactionStack.length + if (reactionLen === 0) return + const current = ReactionStack[reactionLen - 1] if (isUntracking()) return if (current) { DependencyCollected.value = true diff --git a/packages/reactive/src/tree.ts b/packages/reactive/src/tree.ts index 2523a65a3b3..3c17e132a9f 100644 --- a/packages/reactive/src/tree.ts +++ b/packages/reactive/src/tree.ts @@ -1,4 +1,5 @@ -import { RawNode, ProxyRaw } from './environment' +import { ObModelSymbol, ObModelNodeSymbol, RawNode } from './environment' +import { raw as getRaw } from './externals' import { PropertyKey, IOperation } from './types' export class DataChange { node: DataNode @@ -39,7 +40,7 @@ export class DataNode { } get targetRaw() { - return ProxyRaw.get(this.target) || this.target + return getRaw(this.target) } get parent() { @@ -66,15 +67,22 @@ export class DataNode { } export const getDataNode = (raw: any) => { + if (raw?.[ObModelNodeSymbol]) { + return raw[ObModelNodeSymbol] + } return RawNode.get(raw) } export const setDataNode = (raw: any, node: DataNode) => { + if (raw?.[ObModelSymbol]) { + raw[ObModelNodeSymbol] = node + return + } RawNode.set(raw, node) } export const buildDataTree = (target: any, key: PropertyKey, value: any) => { - const raw = ProxyRaw.get(value) || value + const raw = getRaw(value) const currentNode = getDataNode(raw) if (currentNode) return currentNode setDataNode(raw, new DataNode(target, key, value))