vue3-yuanma/packages/reactivity/src/reactive.ts

139 lines
3.9 KiB
TypeScript
Raw Normal View History

2019-06-12 00:03:50 +08:00
import { isObject } from '@vue/shared'
import { mutableHandlers, immutableHandlers } from './baseHandlers'
import {
mutableCollectionHandlers,
immutableCollectionHandlers
} from './collectionHandlers'
import { UnwrapNestedRefs } from './ref'
2018-11-14 00:03:35 +08:00
import { ReactiveEffect } from './effect'
2018-09-19 23:35:38 +08:00
// 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.
2018-11-14 00:03:35 +08:00
export type Dep = Set<ReactiveEffect>
2018-09-19 23:35:38 +08:00
export type KeyToDepMap = Map<string | symbol, Dep>
export const targetMap: WeakMap<any, KeyToDepMap> = new WeakMap()
// WeakMaps that store {raw <-> observed} pairs.
2019-06-12 00:03:50 +08:00
const rawToObserved: WeakMap<any, any> = new WeakMap()
const observedToRaw: WeakMap<any, any> = new WeakMap()
const rawToImmutable: WeakMap<any, any> = new WeakMap()
const immutableToRaw: WeakMap<any, any> = new WeakMap()
2018-09-19 23:35:38 +08:00
// WeakSets for values that are marked immutable or non-reactive during
// observable creation.
2019-06-12 00:03:50 +08:00
const immutableValues: WeakSet<any> = new WeakSet()
const nonReactiveValues: WeakSet<any> = new WeakSet()
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)
)
}
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
2019-06-12 00:03:50 +08:00
// 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)) {
2019-08-16 21:42:46 +08:00
return immutable(target)
2019-06-12 00:03:50 +08:00
}
2019-08-20 21:58:10 +08:00
return createReactiveObject(
2019-06-12 00:03:50 +08:00
target,
rawToObserved,
observedToRaw,
mutableHandlers,
mutableCollectionHandlers
)
}
2019-06-12 00:03:50 +08:00
export function immutable<T extends object>(
target: T
): Readonly<UnwrapNestedRefs<T>>
export function immutable(target: object) {
2019-06-12 00:03:50 +08:00
// value is a mutable observable, retrive its original and return
// a readonly version.
if (observedToRaw.has(target)) {
target = observedToRaw.get(target)
}
2019-08-20 21:58:10 +08:00
return createReactiveObject(
2019-06-12 00:03:50 +08:00
target,
rawToImmutable,
immutableToRaw,
immutableHandlers,
immutableCollectionHandlers
)
}
2019-06-12 00:03:50 +08:00
2019-08-20 21:58:10 +08:00
function createReactiveObject(
2019-06-12 00:03:50 +08:00
target: any,
toProxy: WeakMap<any, any>,
toRaw: WeakMap<any, any>,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
if (!isObject(target)) {
if (__DEV__) {
2019-08-20 21:58:10 +08:00
console.warn(`value cannot be made reactive: ${String(target)}`)
2019-06-12 00:03:50 +08:00
}
return 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)
if (!targetMap.has(target)) {
targetMap.set(target, new Map())
}
return observed
}
2019-08-16 21:42:46 +08:00
export function isReactive(value: any): boolean {
2019-06-12 00:03:50 +08:00
return observedToRaw.has(value) || immutableToRaw.has(value)
}
2019-08-16 21:42:46 +08:00
export function isImmutable(value: any): boolean {
2019-06-12 00:03:50 +08:00
return immutableToRaw.has(value)
}
export function toRaw<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
}