import { isObject, toTypeString } from '@vue/shared' import { mutableHandlers, readonlyHandlers } from './baseHandlers' import { mutableCollectionHandlers, readonlyCollectionHandlers } from './collectionHandlers' import { ReactiveEffect } from './effect' import { UnwrapRef, Ref } from './ref' import { makeMap } from '@vue/shared' // 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 export type KeyToDepMap = Map export const targetMap = new WeakMap() // WeakMaps that store {raw <-> observed} pairs. const rawToReactive = new WeakMap() const reactiveToRaw = new WeakMap() const rawToReadonly = new WeakMap() const readonlyToRaw = new WeakMap() // WeakSets for values that are marked readonly or non-reactive during // observable creation. const readonlyValues = new WeakSet() const nonReactiveValues = new WeakSet() const collectionTypes = new Set([Set, Map, WeakMap, WeakSet]) const isObservableType = /*#__PURE__*/ makeMap( ['Object', 'Array', 'Map', 'Set', 'WeakMap', 'WeakSet'] .map(t => `[object ${t}]`) .join(',') ) const canObserve = (value: any): boolean => { return ( !value._isVue && !value._isVNode && isObservableType(toTypeString(value)) && !nonReactiveValues.has(value) ) } // only unwrap nested ref type UnwrapNestedRefs = T extends Ref ? T : UnwrapRef export function reactive(target: T): UnwrapNestedRefs export function reactive(target: object) { // if trying to observe a readonly proxy, return the readonly version. if (readonlyToRaw.has(target)) { return target } // target is explicitly marked as readonly by user if (readonlyValues.has(target)) { return readonly(target) } return createReactiveObject( target, rawToReactive, reactiveToRaw, mutableHandlers, mutableCollectionHandlers ) } export function readonly( target: T ): Readonly> { // value is a mutable observable, retrieve its original and return // a readonly version. if (reactiveToRaw.has(target)) { target = reactiveToRaw.get(target) } return createReactiveObject( target, rawToReadonly, readonlyToRaw, readonlyHandlers, readonlyCollectionHandlers ) } function createReactiveObject( target: any, toProxy: WeakMap, toRaw: WeakMap, baseHandlers: ProxyHandler, collectionHandlers: ProxyHandler ) { if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } 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 } export function isReactive(value: any): boolean { return reactiveToRaw.has(value) || readonlyToRaw.has(value) } export function isReadonly(value: any): boolean { return readonlyToRaw.has(value) } export function toRaw(observed: T): T { return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed } export function markReadonly(value: T): T { readonlyValues.add(value) return value } export function markNonReactive(value: T): T { nonReactiveValues.add(value) return value }