init (graduate from prototype)
This commit is contained in:
164
packages/observer/src/autorun.ts
Normal file
164
packages/observer/src/autorun.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
import { OperationTypes } from './operations'
|
||||
import { Dep, KeyToDepMap, targetMap } from './state'
|
||||
|
||||
export interface Autorun {
|
||||
(): any
|
||||
isAutorun: true
|
||||
active: boolean
|
||||
raw: Function
|
||||
deps: Array<Dep>
|
||||
scheduler?: Scheduler
|
||||
onTrack?: Debugger
|
||||
onTrigger?: Debugger
|
||||
}
|
||||
|
||||
export interface AutorunOptions {
|
||||
lazy?: boolean
|
||||
scheduler?: Scheduler
|
||||
onTrack?: Debugger
|
||||
onTrigger?: Debugger
|
||||
}
|
||||
|
||||
export type Scheduler = (run: () => any) => void
|
||||
|
||||
export type DebuggerEvent = {
|
||||
runner: Autorun
|
||||
target: any
|
||||
type: OperationTypes
|
||||
key: string | symbol | undefined
|
||||
}
|
||||
|
||||
export type Debugger = (event: DebuggerEvent) => void
|
||||
|
||||
export const activeAutorunStack: Autorun[] = []
|
||||
|
||||
const ITERATE_KEY = Symbol('iterate')
|
||||
|
||||
export function createAutorun(fn: Function, options: AutorunOptions): Autorun {
|
||||
const runner = function runner(...args): any {
|
||||
return run(runner as Autorun, fn, args)
|
||||
} as Autorun
|
||||
runner.active = true
|
||||
runner.raw = fn
|
||||
runner.scheduler = options.scheduler
|
||||
runner.onTrack = options.onTrack
|
||||
runner.onTrigger = options.onTrigger
|
||||
runner.deps = []
|
||||
return runner
|
||||
}
|
||||
|
||||
function run(runner: Autorun, fn: Function, args: any[]): any {
|
||||
if (!runner.active) {
|
||||
return fn(...args)
|
||||
}
|
||||
if (activeAutorunStack.indexOf(runner) === -1) {
|
||||
cleanup(runner)
|
||||
try {
|
||||
activeAutorunStack.push(runner)
|
||||
return fn(...args)
|
||||
} finally {
|
||||
activeAutorunStack.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function cleanup(runner: Autorun) {
|
||||
for (let i = 0; i < runner.deps.length; i++) {
|
||||
runner.deps[i].delete(runner)
|
||||
}
|
||||
runner.deps = []
|
||||
}
|
||||
|
||||
export function track(
|
||||
target: any,
|
||||
type: OperationTypes,
|
||||
key?: string | symbol
|
||||
) {
|
||||
const runner = activeAutorunStack[activeAutorunStack.length - 1]
|
||||
if (runner) {
|
||||
if (type === OperationTypes.ITERATE) {
|
||||
key = ITERATE_KEY
|
||||
}
|
||||
// keyMap must exist because only an observed target can call this function
|
||||
const depsMap = targetMap.get(target) as KeyToDepMap
|
||||
let dep = depsMap.get(key as string | symbol)
|
||||
if (!dep) {
|
||||
depsMap.set(key as string | symbol, (dep = new Set()))
|
||||
}
|
||||
if (!dep.has(runner)) {
|
||||
dep.add(runner)
|
||||
runner.deps.push(dep)
|
||||
if (__DEV__ && runner.onTrack) {
|
||||
runner.onTrack({
|
||||
runner,
|
||||
target,
|
||||
type,
|
||||
key
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function trigger(
|
||||
target: any,
|
||||
type: OperationTypes,
|
||||
key?: string | symbol,
|
||||
extraInfo?: any
|
||||
) {
|
||||
const depsMap = targetMap.get(target) as KeyToDepMap
|
||||
const runners = new Set()
|
||||
if (type === OperationTypes.CLEAR) {
|
||||
// collection being cleared, trigger all runners for target
|
||||
depsMap.forEach(dep => {
|
||||
addRunners(runners, dep)
|
||||
})
|
||||
} else {
|
||||
// schedule runs for SET | ADD | DELETE
|
||||
addRunners(runners, depsMap.get(key as string | symbol))
|
||||
// also run for iteration key on ADD | DELETE
|
||||
if (type === OperationTypes.ADD || type === OperationTypes.DELETE) {
|
||||
const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY
|
||||
addRunners(runners, depsMap.get(iterationKey))
|
||||
}
|
||||
}
|
||||
runners.forEach(runner => {
|
||||
scheduleRun(runner, target, type, key, extraInfo)
|
||||
})
|
||||
}
|
||||
|
||||
function addRunners(
|
||||
runners: Set<Autorun>,
|
||||
runnersToAdd: Set<Autorun> | undefined
|
||||
) {
|
||||
if (runnersToAdd !== void 0) {
|
||||
runnersToAdd.forEach(runners.add, runners)
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleRun(
|
||||
runner: Autorun,
|
||||
target: any,
|
||||
type: OperationTypes,
|
||||
key: string | symbol | undefined,
|
||||
extraInfo: any
|
||||
) {
|
||||
if (__DEV__ && runner.onTrigger) {
|
||||
runner.onTrigger(
|
||||
Object.assign(
|
||||
{
|
||||
runner,
|
||||
target,
|
||||
key,
|
||||
type
|
||||
},
|
||||
extraInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
if (runner.scheduler !== void 0) {
|
||||
runner.scheduler(runner)
|
||||
} else {
|
||||
runner()
|
||||
}
|
||||
}
|
||||
120
packages/observer/src/baseHandlers.ts
Normal file
120
packages/observer/src/baseHandlers.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { observable, immutable, unwrap } from './index'
|
||||
import { OperationTypes } from './operations'
|
||||
import { track, trigger } from './autorun'
|
||||
import { LOCKED } from './lock'
|
||||
|
||||
const hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
|
||||
const builtInSymbols = new Set(
|
||||
Object.getOwnPropertyNames(Symbol)
|
||||
.map(key => (Symbol as any)[key])
|
||||
.filter(value => typeof value === 'symbol')
|
||||
)
|
||||
|
||||
function get(
|
||||
target: any,
|
||||
key: string | symbol,
|
||||
receiver: any,
|
||||
toObsevable: (t: any) => any
|
||||
) {
|
||||
const res = Reflect.get(target, key, receiver)
|
||||
if (typeof key === 'symbol' && builtInSymbols.has(key)) {
|
||||
return res
|
||||
}
|
||||
track(target, OperationTypes.GET, key)
|
||||
return res !== null && typeof res === 'object' ? toObsevable(res) : res
|
||||
}
|
||||
|
||||
function set(
|
||||
target: any,
|
||||
key: string | symbol,
|
||||
value: any,
|
||||
receiver: any
|
||||
): boolean {
|
||||
value = unwrap(value)
|
||||
const hadKey = hasOwnProperty.call(target, key)
|
||||
const oldValue = target[key]
|
||||
const result = Reflect.set(target, key, value, receiver)
|
||||
// don't trigger if target is something up in the prototype chain of original
|
||||
if (target === unwrap(receiver)) {
|
||||
if (__DEV__) {
|
||||
const extraInfo = { oldValue, newValue: value }
|
||||
if (!hadKey) {
|
||||
trigger(target, OperationTypes.ADD, key, extraInfo)
|
||||
} else if (value !== oldValue) {
|
||||
trigger(target, OperationTypes.SET, key, extraInfo)
|
||||
}
|
||||
} else {
|
||||
if (!hadKey) {
|
||||
trigger(target, OperationTypes.ADD, key)
|
||||
} else if (value !== oldValue) {
|
||||
trigger(target, OperationTypes.SET, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function deleteProperty(target: any, key: string | symbol): boolean {
|
||||
const hadKey = hasOwnProperty.call(target, key)
|
||||
const oldValue = target[key]
|
||||
const result = Reflect.deleteProperty(target, key)
|
||||
if (hadKey) {
|
||||
if (__DEV__) {
|
||||
trigger(target, OperationTypes.DELETE, key, { oldValue })
|
||||
} else {
|
||||
trigger(target, OperationTypes.DELETE, key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function has(target: any, key: string | symbol): boolean {
|
||||
const result = Reflect.has(target, key)
|
||||
track(target, OperationTypes.HAS, key)
|
||||
return result
|
||||
}
|
||||
|
||||
function ownKeys(target: any): (string | number | symbol)[] {
|
||||
track(target, OperationTypes.ITERATE)
|
||||
return Reflect.ownKeys(target)
|
||||
}
|
||||
|
||||
export const mutableHandlers: ProxyHandler<any> = {
|
||||
get: (target: any, key: string | symbol, receiver: any) =>
|
||||
get(target, key, receiver, observable),
|
||||
set,
|
||||
deleteProperty,
|
||||
has,
|
||||
ownKeys
|
||||
}
|
||||
|
||||
export const immutableHandlers: ProxyHandler<any> = {
|
||||
get: (target: any, key: string | symbol, receiver: any) =>
|
||||
get(target, key, receiver, LOCKED ? immutable : observable),
|
||||
|
||||
set(target: any, key: string | symbol, value: any, receiver: any): boolean {
|
||||
if (LOCKED) {
|
||||
if (__DEV__) {
|
||||
console.warn(`Set operation failed: target is immutable.`, target)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return set(target, key, value, receiver)
|
||||
}
|
||||
},
|
||||
|
||||
deleteProperty(target: any, key: string | symbol): boolean {
|
||||
if (LOCKED) {
|
||||
if (__DEV__) {
|
||||
console.warn(`Delete operation failed: target is immutable.`, target)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return deleteProperty(target, key)
|
||||
}
|
||||
},
|
||||
|
||||
has,
|
||||
ownKeys
|
||||
}
|
||||
161
packages/observer/src/collectionHandlers.ts
Normal file
161
packages/observer/src/collectionHandlers.ts
Normal file
@@ -0,0 +1,161 @@
|
||||
import { unwrap } from './index'
|
||||
import { track, trigger } from './autorun'
|
||||
import { OperationTypes } from './operations'
|
||||
|
||||
function instrument(
|
||||
target: any,
|
||||
key: string | symbol,
|
||||
args: any[],
|
||||
type: OperationTypes
|
||||
) {
|
||||
target = unwrap(target)
|
||||
const proto: any = Reflect.getPrototypeOf(target)
|
||||
track(target, type)
|
||||
return proto[key].apply(target, args)
|
||||
}
|
||||
|
||||
function get(key: string | symbol) {
|
||||
return instrument(this, key, [key], OperationTypes.GET)
|
||||
}
|
||||
|
||||
function has(key: string | symbol): boolean {
|
||||
return instrument(this, key, [key], OperationTypes.HAS)
|
||||
}
|
||||
|
||||
function size(target: any) {
|
||||
target = unwrap(target)
|
||||
const proto = Reflect.getPrototypeOf(target)
|
||||
track(target, OperationTypes.ITERATE)
|
||||
return Reflect.get(proto, 'size', target)
|
||||
}
|
||||
|
||||
function makeWarning(type: OperationTypes) {
|
||||
return function() {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
`${type} operation failed: target is immutable.`,
|
||||
unwrap(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mutableInstrumentations: any = {
|
||||
get,
|
||||
has,
|
||||
|
||||
get size() {
|
||||
return size(this)
|
||||
},
|
||||
|
||||
add(key: any) {
|
||||
const target = unwrap(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const hadKey = proto.has.call(target, key)
|
||||
const result = proto.add.apply(target, arguments)
|
||||
if (!hadKey) {
|
||||
if (__DEV__) {
|
||||
trigger(target, OperationTypes.ADD, key, { value: key })
|
||||
} else {
|
||||
trigger(target, OperationTypes.ADD, key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
|
||||
set(key: any, value: any) {
|
||||
const target = unwrap(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const hadKey = proto.has.call(target, key)
|
||||
const oldValue = proto.get.call(target, key)
|
||||
const result = proto.set.apply(target, arguments)
|
||||
if (__DEV__) {
|
||||
const extraInfo = { oldValue, newValue: value }
|
||||
if (!hadKey) {
|
||||
trigger(target, OperationTypes.ADD, key, extraInfo)
|
||||
} else {
|
||||
trigger(target, OperationTypes.SET, key, extraInfo)
|
||||
}
|
||||
} else {
|
||||
if (!hadKey) {
|
||||
trigger(target, OperationTypes.ADD, key)
|
||||
} else {
|
||||
trigger(target, OperationTypes.SET, key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
|
||||
delete(key: any) {
|
||||
const target = unwrap(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const hadKey = proto.has.call(target, key)
|
||||
const oldValue = proto.get ? proto.get.call(target, key) : undefined
|
||||
// forward the operation before queueing reactions
|
||||
const result = proto.delete.apply(target, arguments)
|
||||
if (hadKey) {
|
||||
if (__DEV__) {
|
||||
trigger(target, OperationTypes.DELETE, key, { oldValue })
|
||||
} else {
|
||||
trigger(target, OperationTypes.DELETE, key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
},
|
||||
|
||||
clear() {
|
||||
const target = unwrap(this)
|
||||
const proto: any = Reflect.getPrototypeOf(this)
|
||||
const hadItems = target.size !== 0
|
||||
const oldTarget = target instanceof Map ? new Map(target) : new Set(target)
|
||||
// forward the operation before queueing reactions
|
||||
const result = proto.clear.apply(target, arguments)
|
||||
if (hadItems) {
|
||||
if (__DEV__) {
|
||||
trigger(target, OperationTypes.CLEAR, void 0, { oldTarget })
|
||||
} else {
|
||||
trigger(target, OperationTypes.CLEAR)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
const immutableInstrumentations: any = {
|
||||
get,
|
||||
has,
|
||||
get size() {
|
||||
return size(this)
|
||||
},
|
||||
add: makeWarning(OperationTypes.ADD),
|
||||
set: makeWarning(OperationTypes.SET),
|
||||
delete: makeWarning(OperationTypes.DELETE),
|
||||
clear: makeWarning(OperationTypes.CLEAR)
|
||||
}
|
||||
;['forEach', 'keys', 'values', 'entries', Symbol.iterator].forEach(key => {
|
||||
mutableInstrumentations[key] = immutableInstrumentations[key] = function(
|
||||
...args: any[]
|
||||
) {
|
||||
return instrument(this, key, args, OperationTypes.ITERATE)
|
||||
}
|
||||
})
|
||||
|
||||
function getInstrumented(
|
||||
target: any,
|
||||
key: string | symbol,
|
||||
receiver: any,
|
||||
instrumentations: any
|
||||
) {
|
||||
target = instrumentations.hasOwnProperty(key) ? instrumentations : target
|
||||
return Reflect.get(target, key, receiver)
|
||||
}
|
||||
|
||||
export const mutableCollectionHandlers: ProxyHandler<any> = {
|
||||
get: (target: any, key: string | symbol, receiver: any) =>
|
||||
getInstrumented(target, key, receiver, mutableInstrumentations)
|
||||
}
|
||||
|
||||
export const immutableCollectionHandlers: ProxyHandler<any> = {
|
||||
get: (target: any, key: string | symbol, receiver: any) =>
|
||||
getInstrumented(target, key, receiver, immutableInstrumentations)
|
||||
}
|
||||
44
packages/observer/src/computed.ts
Normal file
44
packages/observer/src/computed.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { autorun, stop } from './index'
|
||||
import { Autorun, activeAutorunStack } from './autorun'
|
||||
|
||||
export interface ComputedGetter {
|
||||
(): any
|
||||
stop: () => void
|
||||
}
|
||||
|
||||
export function computed(getter: Function, context?: any): ComputedGetter {
|
||||
let dirty: boolean = true
|
||||
let value: any = undefined
|
||||
const runner = autorun(() => getter.call(context, context), {
|
||||
lazy: true,
|
||||
scheduler: () => {
|
||||
dirty = true
|
||||
}
|
||||
})
|
||||
const computedGetter = (() => {
|
||||
if (dirty) {
|
||||
value = runner()
|
||||
dirty = false
|
||||
}
|
||||
// When computed autoruns are accessed in a parent autorun, the parent
|
||||
// should track all the dependencies the computed property has tracked.
|
||||
// This should also apply for chained computed properties.
|
||||
trackChildRun(runner)
|
||||
return value
|
||||
}) as ComputedGetter
|
||||
computedGetter.stop = () => stop(runner)
|
||||
return computedGetter
|
||||
}
|
||||
|
||||
function trackChildRun(childRunner: Autorun) {
|
||||
const parentRunner = activeAutorunStack[activeAutorunStack.length - 1]
|
||||
if (parentRunner) {
|
||||
for (let i = 0; i < childRunner.deps.length; i++) {
|
||||
const dep = childRunner.deps[i]
|
||||
if (!dep.has(parentRunner)) {
|
||||
dep.add(parentRunner)
|
||||
parentRunner.deps.push(dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
152
packages/observer/src/index.ts
Normal file
152
packages/observer/src/index.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import { mutableHandlers, immutableHandlers } from './baseHandlers'
|
||||
|
||||
import {
|
||||
mutableCollectionHandlers,
|
||||
immutableCollectionHandlers
|
||||
} from './collectionHandlers'
|
||||
|
||||
import {
|
||||
targetMap,
|
||||
observedToRaw,
|
||||
rawToObserved,
|
||||
immutableToRaw,
|
||||
rawToImmutable,
|
||||
immutableValues,
|
||||
nonReactiveValues
|
||||
} from './state'
|
||||
|
||||
import {
|
||||
createAutorun,
|
||||
cleanup,
|
||||
Autorun,
|
||||
AutorunOptions,
|
||||
DebuggerEvent
|
||||
} from './autorun'
|
||||
|
||||
export { Autorun, DebuggerEvent }
|
||||
export { computed, ComputedGetter } from './computed'
|
||||
export { lock, unlock } from './lock'
|
||||
|
||||
const EMPTY_OBJ = {}
|
||||
const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
|
||||
const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/
|
||||
|
||||
const canObserve = (value: any): boolean => {
|
||||
return (
|
||||
!value._isVue &&
|
||||
!value._isVNode &&
|
||||
observableValueRE.test(Object.prototype.toString.call(value)) &&
|
||||
!nonReactiveValues.has(value)
|
||||
)
|
||||
}
|
||||
|
||||
type identity = <T>(target: T) => T
|
||||
|
||||
export const observable = ((target: any = {}): any => {
|
||||
// if trying to observe an immutable proxy, return the immutable version.
|
||||
if (immutableToRaw.has(target)) {
|
||||
return target
|
||||
}
|
||||
// target is explicitly marked as immutable by user
|
||||
if (immutableValues.has(target)) {
|
||||
return immutable(target)
|
||||
}
|
||||
return createObservable(
|
||||
target,
|
||||
rawToObserved,
|
||||
observedToRaw,
|
||||
mutableHandlers,
|
||||
mutableCollectionHandlers
|
||||
)
|
||||
}) as identity
|
||||
|
||||
export const immutable = ((target: any = {}): any => {
|
||||
// value is a mutable observable, retrive its original and return
|
||||
// a readonly version.
|
||||
if (observedToRaw.has(target)) {
|
||||
target = observedToRaw.get(target)
|
||||
}
|
||||
return createObservable(
|
||||
target,
|
||||
rawToImmutable,
|
||||
immutableToRaw,
|
||||
immutableHandlers,
|
||||
immutableCollectionHandlers
|
||||
)
|
||||
}) as identity
|
||||
|
||||
function createObservable(
|
||||
target: any,
|
||||
toProxy: WeakMap<any, any>,
|
||||
toRaw: WeakMap<any, any>,
|
||||
baseHandlers: ProxyHandler<any>,
|
||||
collectionHandlers: ProxyHandler<any>
|
||||
) {
|
||||
if ((__DEV__ && target === null) || typeof target !== 'object') {
|
||||
throw new Error(`value is not observable: ${String(target)}`)
|
||||
}
|
||||
// target already has corresponding Proxy
|
||||
let observed = toProxy.get(target)
|
||||
if (observed !== void 0) {
|
||||
return observed
|
||||
}
|
||||
// target is already a Proxy
|
||||
if (toRaw.has(target)) {
|
||||
return target
|
||||
}
|
||||
// only a whitelist of value types can be observed.
|
||||
if (!canObserve(target)) {
|
||||
return target
|
||||
}
|
||||
const handlers = collectionTypes.has(target.constructor)
|
||||
? collectionHandlers
|
||||
: baseHandlers
|
||||
observed = new Proxy(target, handlers)
|
||||
toProxy.set(target, observed)
|
||||
toRaw.set(observed, target)
|
||||
targetMap.set(target, new Map())
|
||||
return observed
|
||||
}
|
||||
|
||||
export function autorun(
|
||||
fn: Function,
|
||||
options: AutorunOptions = EMPTY_OBJ
|
||||
): Autorun {
|
||||
if ((fn as Autorun).isAutorun) {
|
||||
fn = (fn as Autorun).raw
|
||||
}
|
||||
const runner = createAutorun(fn, options)
|
||||
if (!options.lazy) {
|
||||
runner()
|
||||
}
|
||||
return runner
|
||||
}
|
||||
|
||||
export function stop(runner: Autorun) {
|
||||
if (runner.active) {
|
||||
cleanup(runner)
|
||||
runner.active = false
|
||||
}
|
||||
}
|
||||
|
||||
export function isObservable(value: any): boolean {
|
||||
return observedToRaw.has(value) || immutableToRaw.has(value)
|
||||
}
|
||||
|
||||
export function isImmutable(value: any): boolean {
|
||||
return immutableToRaw.has(value)
|
||||
}
|
||||
|
||||
export function unwrap<T>(observed: T): T {
|
||||
return observedToRaw.get(observed) || immutableToRaw.get(observed) || observed
|
||||
}
|
||||
|
||||
export function markImmutable<T>(value: T): T {
|
||||
immutableValues.add(value)
|
||||
return value
|
||||
}
|
||||
|
||||
export function markNonReactive<T>(value: T): T {
|
||||
nonReactiveValues.add(value)
|
||||
return value
|
||||
}
|
||||
10
packages/observer/src/lock.ts
Normal file
10
packages/observer/src/lock.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
// global immutability lock
|
||||
export let LOCKED = true
|
||||
|
||||
export function lock() {
|
||||
LOCKED = true
|
||||
}
|
||||
|
||||
export function unlock() {
|
||||
LOCKED = false
|
||||
}
|
||||
11
packages/observer/src/operations.ts
Normal file
11
packages/observer/src/operations.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export const enum OperationTypes {
|
||||
// using literal strings instead of numbers so that it's easier to inspect
|
||||
// debugger events
|
||||
SET = 'set',
|
||||
ADD = 'add',
|
||||
DELETE = 'delete',
|
||||
CLEAR = 'clear',
|
||||
GET = 'get',
|
||||
HAS = 'has',
|
||||
ITERATE = 'iterate'
|
||||
}
|
||||
20
packages/observer/src/state.ts
Normal file
20
packages/observer/src/state.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Autorun } from './autorun'
|
||||
|
||||
// The main WeakMap that stores {target -> key -> dep} connections.
|
||||
// Conceptually, it's easier to think of a dependency as a Dep class
|
||||
// which maintains a Set of subscribers, but we simply store them as
|
||||
// raw Sets to reduce memory overhead.
|
||||
export type Dep = Set<Autorun>
|
||||
export type KeyToDepMap = Map<string | symbol, Dep>
|
||||
export const targetMap: WeakMap<any, KeyToDepMap> = new WeakMap()
|
||||
|
||||
// WeakMaps that store {raw <-> observed} pairs.
|
||||
export const rawToObserved: WeakMap<any, any> = new WeakMap()
|
||||
export const observedToRaw: WeakMap<any, any> = new WeakMap()
|
||||
export const rawToImmutable: WeakMap<any, any> = new WeakMap()
|
||||
export const immutableToRaw: WeakMap<any, any> = new WeakMap()
|
||||
|
||||
// WeakSets for values that are marked immutable or non-reactive during
|
||||
// observable creation.
|
||||
export const immutableValues: WeakSet<any> = new WeakSet()
|
||||
export const nonReactiveValues: WeakSet<any> = new WeakSet()
|
||||
Reference in New Issue
Block a user