2019-06-12 00:03:50 +08:00
|
|
|
import { isObject } from '@vue/shared'
|
2019-08-23 21:38:32 +08:00
|
|
|
import { mutableHandlers, readonlyHandlers } from './baseHandlers'
|
2019-06-12 00:03:50 +08:00
|
|
|
|
|
|
|
import {
|
|
|
|
mutableCollectionHandlers,
|
2019-08-23 21:38:32 +08:00
|
|
|
readonlyCollectionHandlers
|
2019-06-12 00:03:50 +08:00
|
|
|
} from './collectionHandlers'
|
|
|
|
|
2019-08-22 00:01:05 +08:00
|
|
|
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-08-23 21:38:32 +08:00
|
|
|
const rawToReactive: WeakMap<any, any> = new WeakMap()
|
|
|
|
const reactiveToRaw: WeakMap<any, any> = new WeakMap()
|
|
|
|
const rawToReadonly: WeakMap<any, any> = new WeakMap()
|
|
|
|
const readonlyToRaw: WeakMap<any, any> = new WeakMap()
|
2018-09-19 23:35:38 +08:00
|
|
|
|
2019-08-23 21:38:32 +08:00
|
|
|
// WeakSets for values that are marked readonly or non-reactive during
|
2018-09-19 23:35:38 +08:00
|
|
|
// observable creation.
|
2019-08-23 21:38:32 +08:00
|
|
|
const readonlyValues: WeakSet<any> = new WeakSet()
|
2019-06-12 00:03:50 +08:00
|
|
|
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)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-22 00:01:05 +08:00
|
|
|
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
|
|
|
|
export function reactive(target: object) {
|
2019-08-23 21:38:32 +08:00
|
|
|
// if trying to observe a readonly proxy, return the readonly version.
|
|
|
|
if (readonlyToRaw.has(target)) {
|
2019-06-12 00:03:50 +08:00
|
|
|
return target
|
|
|
|
}
|
2019-08-23 21:38:32 +08:00
|
|
|
// target is explicitly marked as readonly by user
|
|
|
|
if (readonlyValues.has(target)) {
|
|
|
|
return readonly(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,
|
2019-08-23 21:38:32 +08:00
|
|
|
rawToReactive,
|
|
|
|
reactiveToRaw,
|
2019-06-12 00:03:50 +08:00
|
|
|
mutableHandlers,
|
|
|
|
mutableCollectionHandlers
|
|
|
|
)
|
2019-08-22 00:01:05 +08:00
|
|
|
}
|
2019-06-12 00:03:50 +08:00
|
|
|
|
2019-08-23 21:38:32 +08:00
|
|
|
export function readonly<T extends object>(
|
2019-08-22 00:01:05 +08:00
|
|
|
target: T
|
|
|
|
): Readonly<UnwrapNestedRefs<T>>
|
2019-08-23 21:38:32 +08:00
|
|
|
export function readonly(target: object) {
|
2019-06-12 00:03:50 +08:00
|
|
|
// value is a mutable observable, retrive its original and return
|
|
|
|
// a readonly version.
|
2019-08-23 21:38:32 +08:00
|
|
|
if (reactiveToRaw.has(target)) {
|
|
|
|
target = reactiveToRaw.get(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,
|
2019-08-23 21:38:32 +08:00
|
|
|
rawToReadonly,
|
|
|
|
readonlyToRaw,
|
|
|
|
readonlyHandlers,
|
|
|
|
readonlyCollectionHandlers
|
2019-06-12 00:03:50 +08:00
|
|
|
)
|
2019-08-22 00:01:05 +08:00
|
|
|
}
|
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-08-23 21:38:32 +08:00
|
|
|
return reactiveToRaw.has(value) || readonlyToRaw.has(value)
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
2019-08-23 21:38:32 +08:00
|
|
|
export function isReadonly(value: any): boolean {
|
|
|
|
return readonlyToRaw.has(value)
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function toRaw<T>(observed: T): T {
|
2019-08-23 21:38:32 +08:00
|
|
|
return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
2019-08-23 21:38:32 +08:00
|
|
|
export function markReadonly<T>(value: T): T {
|
|
|
|
readonlyValues.add(value)
|
2019-06-12 00:03:50 +08:00
|
|
|
return value
|
|
|
|
}
|
|
|
|
|
|
|
|
export function markNonReactive<T>(value: T): T {
|
|
|
|
nonReactiveValues.add(value)
|
|
|
|
return value
|
|
|
|
}
|