import { isObject, toRawType, def, hasOwn } from '@vue/shared' import { mutableHandlers, readonlyHandlers, shallowReactiveHandlers, shallowReadonlyHandlers } from './baseHandlers' import { mutableCollectionHandlers, readonlyCollectionHandlers } from './collectionHandlers' import { UnwrapRef, Ref } from './ref' import { makeMap } from '@vue/shared' export const enum ReactiveFlags { skip = '__v_skip', isReactive = '__v_isReactive', isReadonly = '__v_isReadonly', raw = '__v_raw', reactive = '__v_reactive', readonly = '__v_readonly' } interface Target { __v_skip?: boolean __v_isReactive?: boolean __v_isReadonly?: boolean __v_raw?: any __v_reactive?: any __v_readonly?: any } const collectionTypes = new Set([Set, Map, WeakMap, WeakSet]) const isObservableType = /*#__PURE__*/ makeMap( 'Object,Array,Map,Set,WeakMap,WeakSet' ) const canObserve = (value: Target): boolean => { return ( !value.__v_skip && isObservableType(toRawType(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 (target && (target as Target).__v_isReadonly) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers ) } // 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, false, shallowReactiveHandlers, mutableCollectionHandlers ) } export function readonly( target: T ): Readonly> { return createReactiveObject( target, true, 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, true, shallowReadonlyHandlers, readonlyCollectionHandlers ) } function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler, collectionHandlers: ProxyHandler ) { if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } // target is already a Proxy, return it. // excpetion: calling readonly() on a reactive object if (target.__v_raw && !(isReadonly && target.__v_isReactive)) { return target } // target already has corresponding Proxy if ( hasOwn(target, isReadonly ? ReactiveFlags.readonly : ReactiveFlags.reactive) ) { return isReadonly ? target.__v_readonly : target.__v_reactive } // only a whitelist of value types can be observed. if (!canObserve(target)) { return target } const observed = new Proxy( target, collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers ) def( target, isReadonly ? ReactiveFlags.readonly : ReactiveFlags.reactive, observed ) return observed } export function isReactive(value: unknown): boolean { if (isReadonly(value)) { return isReactive((value as Target).__v_raw) } return !!(value && (value as Target).__v_isReactive) } export function isReadonly(value: unknown): boolean { return !!(value && (value as Target).__v_isReadonly) } export function isProxy(value: unknown): boolean { return isReactive(value) || isReadonly(value) } export function toRaw(observed: T): T { return (observed && toRaw((observed as Target).__v_raw)) || observed } export function markRaw(value: T): T { def(value, ReactiveFlags.skip, true) return value }