vue3-yuanma/packages/reactivity/src/reactive.ts

202 lines
5.4 KiB
TypeScript
Raw Normal View History

import { isObject, toRawType, def, hasOwn } from '@vue/shared'
import {
mutableHandlers,
readonlyHandlers,
shallowReactiveHandlers,
shallowReadonlyHandlers
} from './baseHandlers'
2019-06-12 00:03:50 +08:00
import {
mutableCollectionHandlers,
readonlyCollectionHandlers,
shallowCollectionHandlers
2019-06-12 00:03:50 +08:00
} from './collectionHandlers'
import { UnwrapRef, Ref } from './ref'
2018-09-19 23:35:38 +08:00
export const enum ReactiveFlags {
SKIP = '__v_skip',
IS_REACTIVE = '__v_isReactive',
IS_READONLY = '__v_isReadonly',
RAW = '__v_raw',
REACTIVE = '__v_reactive',
READONLY = '__v_readonly'
}
export interface Target {
[ReactiveFlags.SKIP]?: boolean
[ReactiveFlags.IS_REACTIVE]?: boolean
[ReactiveFlags.IS_READONLY]?: boolean
[ReactiveFlags.RAW]?: any
[ReactiveFlags.REACTIVE]?: any
[ReactiveFlags.READONLY]?: any
}
2019-06-12 00:03:50 +08:00
const enum TargetType {
INVALID = 0,
COMMON = 1,
COLLECTION = 2
}
2019-06-12 00:03:50 +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>
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.
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,
false,
2019-06-12 00:03:50 +08:00
mutableHandlers,
mutableCollectionHandlers
)
}
2019-06-12 00:03:50 +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,
false,
shallowReactiveHandlers,
shallowCollectionHandlers
)
}
type Primitive = string | number | boolean | bigint | symbol | undefined | null
type Builtin = Primitive | Function | Date | Error | RegExp
export type DeepReadonly<T> = T extends Builtin
? 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>(
target: T
): DeepReadonly<UnwrapNestedRefs<T>> {
2019-08-20 21:58:10 +08:00
return createReactiveObject(
2019-06-12 00:03:50 +08:00
target,
true,
2019-08-23 21:38:32 +08:00
readonlyHandlers,
readonlyCollectionHandlers
2019-06-12 00:03:50 +08:00
)
}
2019-06-12 00:03:50 +08:00
// 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<T extends object>(
target: T
): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
return createReactiveObject(
target,
true,
shallowReadonlyHandlers,
readonlyCollectionHandlers
)
}
2019-08-20 21:58:10 +08:00
function createReactiveObject(
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
}
// target is already a Proxy, return it.
2020-05-04 16:30:24 +08:00
// exception: calling readonly() on a reactive object
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
2019-06-12 00:03:50 +08:00
// target already has corresponding Proxy
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.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
2019-06-12 00:03:50 +08:00
return target
}
const observed = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
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 {
if (isReadonly(value)) {
return isReactive((value as Target)[ReactiveFlags.RAW])
}
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 {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
2019-06-12 00:03:50 +08:00
}
export function isProxy(value: unknown): boolean {
return isReactive(value) || isReadonly(value)
}
2019-06-12 00:03:50 +08:00
export function toRaw<T>(observed: T): T {
return (
(observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
)
2019-06-12 00:03:50 +08:00
}
export function markRaw<T extends object>(value: T): T {
def(value, ReactiveFlags.SKIP, true)
2019-06-12 00:03:50 +08:00
return value
}