2020-08-18 00:17:46 +08:00
|
|
|
import { isObject, toRawType, def, hasOwn } 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,
|
|
|
|
shallowCollectionHandlers
|
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',
|
|
|
|
RAW = '__v_raw',
|
|
|
|
REACTIVE = '__v_reactive',
|
|
|
|
READONLY = '__v_readonly'
|
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
|
|
|
|
[ReactiveFlags.REACTIVE]?: any
|
|
|
|
[ReactiveFlags.READONLY]?: any
|
2020-05-03 04:16:51 +08:00
|
|
|
}
|
2019-06-12 00:03:50 +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
|
|
|
|
type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
|
|
|
|
|
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,
|
|
|
|
mutableCollectionHandlers
|
|
|
|
)
|
2019-08-22 00:01:05 +08:00
|
|
|
}
|
2019-06-12 00:03:50 +08:00
|
|
|
|
2020-04-15 11:49:46 +08:00
|
|
|
// 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<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,
|
2020-05-18 23:17:37 +08:00
|
|
|
shallowCollectionHandlers
|
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>
|
|
|
|
? 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>
|
|
|
|
|
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,
|
|
|
|
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-12-03 03:11:12 +08:00
|
|
|
// Return a reactive-copy of the original object, where only the root level
|
2020-01-28 04:15:13 +08:00
|
|
|
// properties are readonly, and does NOT unwrap refs nor recursively convert
|
|
|
|
// returned properties.
|
2019-12-03 03:11:12 +08:00
|
|
|
// This is used for creating the props proxy object for stateful components.
|
|
|
|
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,
|
2019-11-07 01:51:06 +08:00
|
|
|
readonlyCollectionHandlers
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
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>,
|
|
|
|
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
|
|
|
|
}
|
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-07-21 22:33:09 +08:00
|
|
|
const reactiveFlag = isReadonly
|
|
|
|
? ReactiveFlags.READONLY
|
|
|
|
: ReactiveFlags.REACTIVE
|
|
|
|
if (hasOwn(target, reactiveFlag)) {
|
|
|
|
return target[reactiveFlag]
|
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-05-04 03:32:37 +08:00
|
|
|
const observed = new Proxy(
|
|
|
|
target,
|
2020-08-18 00:17:46 +08:00
|
|
|
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
|
2020-05-04 03:32:37 +08:00
|
|
|
)
|
2020-07-21 22:33:09 +08:00
|
|
|
def(target, reactiveFlag, observed)
|
2019-06-12 00:03:50 +08:00
|
|
|
return observed
|
|
|
|
}
|
|
|
|
|
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 {
|
2020-06-12 05:32:44 +08:00
|
|
|
return (
|
2020-06-26 21:32:09 +08:00
|
|
|
(observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
|
2020-06-12 05:32:44 +08:00
|
|
|
)
|
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
|
|
|
|
}
|