import { isObject, toRawType } from '@vue/shared' import { mutableHandlers, readonlyHandlers, shallowReadonlyHandlers, shallowReactiveHandlers } from './baseHandlers' import { mutableCollectionHandlers, readonlyCollectionHandlers } from './collectionHandlers' import { UnwrapRef, Ref } from './ref' import { makeMap } from '@vue/shared' // 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 nonReactiveValues = new WeakSet() const collectionTypes = new Set([Set, Map, WeakMap, WeakSet]) const isObservableType = /*#__PURE__*/ makeMap( 'Object,Array,Map,Set,WeakMap,WeakSet' ) const canObserve = (value: any): boolean => { return ( !value._isVue && !value._isVNode && isObservableType(toRawType(value)) && !nonReactiveValues.has(value) && !Object.isFrozen(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 } 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 ) } // Return a reactive-copy of the original object, where only the root level // properties are readonly, and does NOT unwrap refs nor recursively convert // returned properties. // This is used for creating the props proxy object for stateful components. export function shallowReadonly( target: T ): Readonly<{ [K in keyof T]: UnwrapNestedRefs }> { return createReactiveObject( target, rawToReadonly, readonlyToRaw, shallowReadonlyHandlers, readonlyCollectionHandlers ) } // Return a reactive-copy of the original object, where only the root level // properties are reactive, and does NOT unwrap refs nor recursively convert // returned properties. export function shallowReactive(target: T): T { return createReactiveObject( target, rawToReactive, reactiveToRaw, shallowReactiveHandlers, mutableCollectionHandlers ) } function createReactiveObject( target: unknown, 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) return observed } export function isReactive(value: unknown): boolean { return reactiveToRaw.has(value) || readonlyToRaw.has(value) } export function isReadonly(value: unknown): boolean { return readonlyToRaw.has(value) } export function toRaw(observed: T): T { return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed } export function markNonReactive(value: T): T { nonReactiveValues.add(value) return value }