2020-08-25 03:26:53 +08:00
|
|
|
import { isObject, toRawType, def } from '@vue/shared'
|
2019-11-07 01:51:06 +08:00
|
|
|
import {
|
|
|
|
mutableHandlers,
|
|
|
|
readonlyHandlers,
|
2020-04-15 11:49:46 +08:00
|
|
|
shallowReactiveHandlers,
|
|
|
|
shallowReadonlyHandlers
|
2019-11-07 01:51:06 +08:00
|
|
|
} from './baseHandlers'
|
2019-06-12 00:03:50 +08:00
|
|
|
import {
|
|
|
|
mutableCollectionHandlers,
|
2020-05-18 23:17:37 +08:00
|
|
|
readonlyCollectionHandlers,
|
2021-03-27 03:10:21 +08:00
|
|
|
shallowCollectionHandlers,
|
|
|
|
shallowReadonlyCollectionHandlers
|
2019-06-12 00:03:50 +08:00
|
|
|
} from './collectionHandlers'
|
2020-04-15 10:18:58 +08:00
|
|
|
import { UnwrapRef, Ref } from './ref'
|
2018-09-19 23:35:38 +08:00
|
|
|
|
2020-05-03 04:16:51 +08:00
|
|
|
export const enum ReactiveFlags {
|
2020-06-26 21:32:09 +08:00
|
|
|
SKIP = '__v_skip',
|
|
|
|
IS_REACTIVE = '__v_isReactive',
|
|
|
|
IS_READONLY = '__v_isReadonly',
|
2020-08-25 03:26:53 +08:00
|
|
|
RAW = '__v_raw'
|
2020-05-03 04:16:51 +08:00
|
|
|
}
|
|
|
|
|
2020-08-15 05:37:36 +08:00
|
|
|
export interface Target {
|
2020-06-26 21:32:09 +08:00
|
|
|
[ReactiveFlags.SKIP]?: boolean
|
|
|
|
[ReactiveFlags.IS_REACTIVE]?: boolean
|
|
|
|
[ReactiveFlags.IS_READONLY]?: boolean
|
|
|
|
[ReactiveFlags.RAW]?: any
|
2020-05-03 04:16:51 +08:00
|
|
|
}
|
2019-06-12 00:03:50 +08:00
|
|
|
|
2020-08-25 03:26:53 +08:00
|
|
|
export const reactiveMap = new WeakMap<Target, any>()
|
2021-03-27 03:39:56 +08:00
|
|
|
export const shallowReactiveMap = new WeakMap<Target, any>()
|
2020-08-25 03:26:53 +08:00
|
|
|
export const readonlyMap = new WeakMap<Target, any>()
|
2021-03-27 03:39:56 +08:00
|
|
|
export const shallowReadonlyMap = new WeakMap<Target, any>()
|
2020-08-25 03:26:53 +08:00
|
|
|
|
2020-08-18 00:17:46 +08:00
|
|
|
const enum TargetType {
|
|
|
|
INVALID = 0,
|
|
|
|
COMMON = 1,
|
|
|
|
COLLECTION = 2
|
|
|
|
}
|
2019-06-12 00:03:50 +08:00
|
|
|
|
2020-08-18 00:17:46 +08:00
|
|
|
function targetTypeMap(rawType: string) {
|
|
|
|
switch (rawType) {
|
|
|
|
case 'Object':
|
|
|
|
case 'Array':
|
|
|
|
return TargetType.COMMON
|
|
|
|
case 'Map':
|
|
|
|
case 'Set':
|
|
|
|
case 'WeakMap':
|
|
|
|
case 'WeakSet':
|
|
|
|
return TargetType.COLLECTION
|
|
|
|
default:
|
|
|
|
return TargetType.INVALID
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTargetType(value: Target) {
|
|
|
|
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
|
|
|
|
? TargetType.INVALID
|
|
|
|
: targetTypeMap(toRawType(value))
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
2019-10-14 23:21:09 +08:00
|
|
|
// only unwrap nested ref
|
2021-03-26 05:30:10 +08:00
|
|
|
export type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
|
2019-10-14 23:21:09 +08:00
|
|
|
|
2020-10-24 02:37:09 +08:00
|
|
|
/**
|
|
|
|
* Creates a reactive copy of the original object.
|
|
|
|
*
|
|
|
|
* The reactive conversion is "deep"—it affects all nested properties. In the
|
|
|
|
* ES2015 Proxy based implementation, the returned proxy is **not** equal to the
|
|
|
|
* original object. It is recommended to work exclusively with the reactive
|
|
|
|
* proxy and avoid relying on the original object.
|
|
|
|
*
|
|
|
|
* A reactive object also automatically unwraps refs contained in it, so you
|
|
|
|
* don't need to use `.value` when accessing and mutating their value:
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* const count = ref(0)
|
|
|
|
* const obj = reactive({
|
|
|
|
* count
|
|
|
|
* })
|
|
|
|
*
|
|
|
|
* obj.count++
|
|
|
|
* obj.count // -> 1
|
|
|
|
* count.value // -> 1
|
|
|
|
* ```
|
|
|
|
*/
|
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.
|
2020-06-26 21:32:09 +08:00
|
|
|
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
|
2019-06-12 00:03:50 +08:00
|
|
|
return target
|
|
|
|
}
|
2019-08-20 21:58:10 +08:00
|
|
|
return createReactiveObject(
|
2019-06-12 00:03:50 +08:00
|
|
|
target,
|
2020-05-03 04:16:51 +08:00
|
|
|
false,
|
2019-06-12 00:03:50 +08:00
|
|
|
mutableHandlers,
|
2021-03-27 03:39:56 +08:00
|
|
|
mutableCollectionHandlers,
|
|
|
|
reactiveMap
|
2019-06-12 00:03:50 +08:00
|
|
|
)
|
2019-08-22 00:01:05 +08:00
|
|
|
}
|
2019-06-12 00:03:50 +08:00
|
|
|
|
2020-10-24 02:37:09 +08:00
|
|
|
/**
|
|
|
|
* Return a shallowly-reactive copy of the original object, where only the root
|
|
|
|
* level properties are reactive. It also does not auto-unwrap refs (even at the
|
|
|
|
* root level).
|
|
|
|
*/
|
2020-04-15 11:49:46 +08:00
|
|
|
export function shallowReactive<T extends object>(target: T): T {
|
|
|
|
return createReactiveObject(
|
|
|
|
target,
|
2020-05-03 04:16:51 +08:00
|
|
|
false,
|
2020-04-15 11:49:46 +08:00
|
|
|
shallowReactiveHandlers,
|
2021-03-27 03:39:56 +08:00
|
|
|
shallowCollectionHandlers,
|
|
|
|
shallowReactiveMap
|
2020-04-15 11:49:46 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-07-15 21:27:21 +08:00
|
|
|
type Primitive = string | number | boolean | bigint | symbol | undefined | null
|
|
|
|
type Builtin = Primitive | Function | Date | Error | RegExp
|
2020-07-17 21:28:50 +08:00
|
|
|
export type DeepReadonly<T> = T extends Builtin
|
2020-07-15 21:27:21 +08:00
|
|
|
? T
|
|
|
|
: T extends Map<infer K, infer V>
|
2021-07-20 06:24:18 +08:00
|
|
|
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
|
|
|
|
: T extends ReadonlyMap<infer K, infer V>
|
|
|
|
? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
|
|
|
|
: T extends WeakMap<infer K, infer V>
|
|
|
|
? WeakMap<DeepReadonly<K>, DeepReadonly<V>>
|
|
|
|
: T extends Set<infer U>
|
|
|
|
? ReadonlySet<DeepReadonly<U>>
|
|
|
|
: T extends ReadonlySet<infer U>
|
|
|
|
? ReadonlySet<DeepReadonly<U>>
|
|
|
|
: T extends WeakSet<infer U>
|
|
|
|
? WeakSet<DeepReadonly<U>>
|
|
|
|
: T extends Promise<infer U>
|
|
|
|
? Promise<DeepReadonly<U>>
|
|
|
|
: T extends {}
|
|
|
|
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
|
|
|
|
: Readonly<T>
|
2020-07-15 21:27:21 +08:00
|
|
|
|
2020-10-24 02:37:09 +08:00
|
|
|
/**
|
|
|
|
* Creates a readonly copy of the original object. Note the returned copy is not
|
|
|
|
* made reactive, but `readonly` can be called on an already reactive object.
|
|
|
|
*/
|
2019-08-23 21:38:32 +08:00
|
|
|
export function readonly<T extends object>(
|
2019-08-22 00:01:05 +08:00
|
|
|
target: T
|
2020-07-15 21:27:21 +08:00
|
|
|
): DeepReadonly<UnwrapNestedRefs<T>> {
|
2019-08-20 21:58:10 +08:00
|
|
|
return createReactiveObject(
|
2019-06-12 00:03:50 +08:00
|
|
|
target,
|
2020-05-03 04:16:51 +08:00
|
|
|
true,
|
2019-08-23 21:38:32 +08:00
|
|
|
readonlyHandlers,
|
2021-03-27 03:39:56 +08:00
|
|
|
readonlyCollectionHandlers,
|
|
|
|
readonlyMap
|
2019-06-12 00:03:50 +08:00
|
|
|
)
|
2019-08-22 00:01:05 +08:00
|
|
|
}
|
2019-06-12 00:03:50 +08:00
|
|
|
|
2020-10-24 02:37:09 +08:00
|
|
|
/**
|
|
|
|
* Returns 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.
|
|
|
|
*/
|
2019-12-03 03:11:12 +08:00
|
|
|
export function shallowReadonly<T extends object>(
|
2019-11-07 01:51:06 +08:00
|
|
|
target: T
|
|
|
|
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
|
|
|
|
return createReactiveObject(
|
|
|
|
target,
|
2020-05-03 04:16:51 +08:00
|
|
|
true,
|
2019-12-03 03:11:12 +08:00
|
|
|
shallowReadonlyHandlers,
|
2021-03-27 03:39:56 +08:00
|
|
|
shallowReadonlyCollectionHandlers,
|
|
|
|
shallowReadonlyMap
|
2019-11-07 01:51:06 +08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-20 21:58:10 +08:00
|
|
|
function createReactiveObject(
|
2020-05-03 04:16:51 +08:00
|
|
|
target: Target,
|
|
|
|
isReadonly: boolean,
|
2019-06-12 00:03:50 +08:00
|
|
|
baseHandlers: ProxyHandler<any>,
|
2021-03-27 03:39:56 +08:00
|
|
|
collectionHandlers: ProxyHandler<any>,
|
|
|
|
proxyMap: WeakMap<Target, any>
|
2019-06-12 00:03:50 +08:00
|
|
|
) {
|
|
|
|
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
|
|
|
|
}
|
2020-05-03 04:16:51 +08:00
|
|
|
// target is already a Proxy, return it.
|
2020-05-04 16:30:24 +08:00
|
|
|
// exception: calling readonly() on a reactive object
|
2020-06-12 05:32:44 +08:00
|
|
|
if (
|
2020-06-26 21:32:09 +08:00
|
|
|
target[ReactiveFlags.RAW] &&
|
|
|
|
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
|
2020-06-12 05:32:44 +08:00
|
|
|
) {
|
2020-05-03 04:16:51 +08:00
|
|
|
return target
|
|
|
|
}
|
2019-06-12 00:03:50 +08:00
|
|
|
// target already has corresponding Proxy
|
2020-08-25 03:26:53 +08:00
|
|
|
const existingProxy = proxyMap.get(target)
|
|
|
|
if (existingProxy) {
|
|
|
|
return existingProxy
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
// only a whitelist of value types can be observed.
|
2020-08-18 00:17:46 +08:00
|
|
|
const targetType = getTargetType(target)
|
|
|
|
if (targetType === TargetType.INVALID) {
|
2019-06-12 00:03:50 +08:00
|
|
|
return target
|
|
|
|
}
|
2020-08-25 03:26:53 +08:00
|
|
|
const proxy = new Proxy(
|
2020-05-04 03:32:37 +08:00
|
|
|
target,
|
2020-08-18 00:17:46 +08:00
|
|
|
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
|
2020-05-04 03:32:37 +08:00
|
|
|
)
|
2020-08-25 03:26:53 +08:00
|
|
|
proxyMap.set(target, proxy)
|
|
|
|
return proxy
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
2019-10-22 23:26:48 +08:00
|
|
|
export function isReactive(value: unknown): boolean {
|
2020-05-03 04:16:51 +08:00
|
|
|
if (isReadonly(value)) {
|
2020-06-26 21:32:09 +08:00
|
|
|
return isReactive((value as Target)[ReactiveFlags.RAW])
|
2020-05-03 04:16:51 +08:00
|
|
|
}
|
2020-06-26 21:32:09 +08:00
|
|
|
return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
2019-10-22 23:26:48 +08:00
|
|
|
export function isReadonly(value: unknown): boolean {
|
2020-06-26 21:32:09 +08:00
|
|
|
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
2020-04-16 04:45:20 +08:00
|
|
|
export function isProxy(value: unknown): boolean {
|
2020-05-03 04:16:51 +08:00
|
|
|
return isReactive(value) || isReadonly(value)
|
2020-04-16 04:45:20 +08:00
|
|
|
}
|
|
|
|
|
2019-06-12 00:03:50 +08:00
|
|
|
export function toRaw<T>(observed: T): T {
|
2021-06-24 05:22:21 +08:00
|
|
|
const raw = observed && (observed as Target)[ReactiveFlags.RAW]
|
|
|
|
return raw ? toRaw(raw) : observed
|
2019-06-12 00:03:50 +08:00
|
|
|
}
|
|
|
|
|
2020-04-16 04:45:20 +08:00
|
|
|
export function markRaw<T extends object>(value: T): T {
|
2020-06-26 21:32:09 +08:00
|
|
|
def(value, ReactiveFlags.SKIP, true)
|
2019-06-12 00:03:50 +08:00
|
|
|
return value
|
|
|
|
}
|