Skip to content

Commit

Permalink
perf(reactive): improve reactive performance (#3430)
Browse files Browse the repository at this point in the history
  • Loading branch information
janryWang authored Sep 30, 2022
1 parent e077e6c commit 5196f45
Show file tree
Hide file tree
Showing 10 changed files with 50 additions and 37 deletions.
11 changes: 5 additions & 6 deletions packages/reactive/src/annotations/computed.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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
}
Expand Down
5 changes: 2 additions & 3 deletions packages/reactive/src/annotations/ref.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ProxyRaw, RawProxy } from '../environment'
import { ObModelSymbol } from '../environment'
import { createAnnotation } from '../internals'
import { buildDataTree } from '../tree'
import {
Expand Down Expand Up @@ -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
})
4 changes: 3 additions & 1 deletion packages/reactive/src/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ export const DependencyCollected = { value: false }
export const PendingReactions = new ArraySet<Reaction>()
export const PendingScopeReactions = new ArraySet<Reaction>()
export const BatchEndpoints = new ArraySet<() => void>()
export const MakeObservableSymbol = Symbol('MakeObservableSymbol')
export const ObserverListeners = new ArraySet<ObservableListener>()
export const MakeObModelSymbol = Symbol('MakeObModelSymbol')
export const ObModelSymbol = Symbol('ObModelSymbol')
export const ObModelNodeSymbol = Symbol('ObModelNodeSymbol')
16 changes: 10 additions & 6 deletions packages/reactive/src/externals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import {
} from './checkers'
import {
ProxyRaw,
MakeObservableSymbol,
MakeObModelSymbol,
DependencyCollected,
ObModelSymbol,
} from './environment'
import { getDataNode } from './tree'
import { Annotation } from './types'
Expand All @@ -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) => {
Expand Down Expand Up @@ -80,7 +81,10 @@ export const markObservable = <T>(target: T): T => {
return target
}

export const raw = <T>(target: T): T => ProxyRaw.get(target as any)
export const raw = <T>(target: T): T => {
if (target?.[ObModelSymbol]) return target[ObModelSymbol]
return ProxyRaw.get(target as any) || target
}

export const toJS = <T>(values: T): T => {
const visited = new WeakSet<any>()
Expand Down Expand Up @@ -119,8 +123,8 @@ export const toJS = <T>(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)
Expand Down
16 changes: 8 additions & 8 deletions packages/reactive/src/internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { isFn, isCollectionType, isNormalType } from './checkers'
import {
RawProxy,
ProxyRaw,
MakeObservableSymbol,
MakeObModelSymbol,
RawShallowProxy,
} from './environment'
import { baseHandlers, collectionHandlers } from './handlers'
Expand Down Expand Up @@ -75,17 +75,17 @@ export const createAnnotation = <T extends (visitor: IVisitor) => 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])
}
}

Expand Down Expand Up @@ -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
}
5 changes: 2 additions & 3 deletions packages/reactive/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@ 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 extends object = any>(
target: Target,
annotations?: Annotations<Target>
): 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)) {
Expand Down
4 changes: 2 additions & 2 deletions packages/reactive/src/observable.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as annotations from './annotations'
import { MakeObservableSymbol } from './environment'
import { MakeObModelSymbol } from './environment'
import { createObservable } from './internals'

export function observable<T extends object>(target: T): T {
Expand All @@ -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
7 changes: 4 additions & 3 deletions packages/reactive/src/observe.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -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)) {
Expand Down
5 changes: 3 additions & 2 deletions packages/reactive/src/reaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 11 additions & 3 deletions packages/reactive/src/tree.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -39,7 +40,7 @@ export class DataNode {
}

get targetRaw() {
return ProxyRaw.get(this.target) || this.target
return getRaw(this.target)
}

get parent() {
Expand All @@ -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))
Expand Down

0 comments on commit 5196f45

Please sign in to comment.