reactive.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import { isObject, toRawType, def, hasOwn } from '@vue/shared'
  2. import {
  3. mutableHandlers,
  4. readonlyHandlers,
  5. shallowReactiveHandlers,
  6. shallowReadonlyHandlers
  7. } from './baseHandlers'
  8. import {
  9. mutableCollectionHandlers,
  10. readonlyCollectionHandlers,
  11. shallowCollectionHandlers
  12. } from './collectionHandlers'
  13. import { UnwrapRef, Ref } from './ref'
  14. export const enum ReactiveFlags {
  15. SKIP = '__v_skip',
  16. IS_REACTIVE = '__v_isReactive',
  17. IS_READONLY = '__v_isReadonly',
  18. RAW = '__v_raw',
  19. REACTIVE = '__v_reactive',
  20. READONLY = '__v_readonly'
  21. }
  22. export interface Target {
  23. [ReactiveFlags.SKIP]?: boolean
  24. [ReactiveFlags.IS_REACTIVE]?: boolean
  25. [ReactiveFlags.IS_READONLY]?: boolean
  26. [ReactiveFlags.RAW]?: any
  27. [ReactiveFlags.REACTIVE]?: any
  28. [ReactiveFlags.READONLY]?: any
  29. }
  30. const enum TargetType {
  31. INVALID = 0,
  32. COMMON = 1,
  33. COLLECTION = 2
  34. }
  35. function targetTypeMap(rawType: string) {
  36. switch (rawType) {
  37. case 'Object':
  38. case 'Array':
  39. return TargetType.COMMON
  40. case 'Map':
  41. case 'Set':
  42. case 'WeakMap':
  43. case 'WeakSet':
  44. return TargetType.COLLECTION
  45. default:
  46. return TargetType.INVALID
  47. }
  48. }
  49. function getTargetType(value: Target) {
  50. return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
  51. ? TargetType.INVALID
  52. : targetTypeMap(toRawType(value))
  53. }
  54. // only unwrap nested ref
  55. type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>
  56. export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
  57. export function reactive(target: object) {
  58. // if trying to observe a readonly proxy, return the readonly version.
  59. if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
  60. return target
  61. }
  62. return createReactiveObject(
  63. target,
  64. false,
  65. mutableHandlers,
  66. mutableCollectionHandlers
  67. )
  68. }
  69. // Return a reactive-copy of the original object, where only the root level
  70. // properties are reactive, and does NOT unwrap refs nor recursively convert
  71. // returned properties.
  72. export function shallowReactive<T extends object>(target: T): T {
  73. return createReactiveObject(
  74. target,
  75. false,
  76. shallowReactiveHandlers,
  77. shallowCollectionHandlers
  78. )
  79. }
  80. type Primitive = string | number | boolean | bigint | symbol | undefined | null
  81. type Builtin = Primitive | Function | Date | Error | RegExp
  82. export type DeepReadonly<T> = T extends Builtin
  83. ? T
  84. : T extends Map<infer K, infer V>
  85. ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
  86. : T extends ReadonlyMap<infer K, infer V>
  87. ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>>
  88. : T extends WeakMap<infer K, infer V>
  89. ? WeakMap<DeepReadonly<K>, DeepReadonly<V>>
  90. : T extends Set<infer U>
  91. ? ReadonlySet<DeepReadonly<U>>
  92. : T extends ReadonlySet<infer U>
  93. ? ReadonlySet<DeepReadonly<U>>
  94. : T extends WeakSet<infer U>
  95. ? WeakSet<DeepReadonly<U>>
  96. : T extends Promise<infer U>
  97. ? Promise<DeepReadonly<U>>
  98. : T extends {}
  99. ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  100. : Readonly<T>
  101. export function readonly<T extends object>(
  102. target: T
  103. ): DeepReadonly<UnwrapNestedRefs<T>> {
  104. return createReactiveObject(
  105. target,
  106. true,
  107. readonlyHandlers,
  108. readonlyCollectionHandlers
  109. )
  110. }
  111. // Return a reactive-copy of the original object, where only the root level
  112. // properties are readonly, and does NOT unwrap refs nor recursively convert
  113. // returned properties.
  114. // This is used for creating the props proxy object for stateful components.
  115. export function shallowReadonly<T extends object>(
  116. target: T
  117. ): Readonly<{ [K in keyof T]: UnwrapNestedRefs<T[K]> }> {
  118. return createReactiveObject(
  119. target,
  120. true,
  121. shallowReadonlyHandlers,
  122. readonlyCollectionHandlers
  123. )
  124. }
  125. function createReactiveObject(
  126. target: Target,
  127. isReadonly: boolean,
  128. baseHandlers: ProxyHandler<any>,
  129. collectionHandlers: ProxyHandler<any>
  130. ) {
  131. if (!isObject(target)) {
  132. if (__DEV__) {
  133. console.warn(`value cannot be made reactive: ${String(target)}`)
  134. }
  135. return target
  136. }
  137. // target is already a Proxy, return it.
  138. // exception: calling readonly() on a reactive object
  139. if (
  140. target[ReactiveFlags.RAW] &&
  141. !(isReadonly && target[ReactiveFlags.IS_REACTIVE])
  142. ) {
  143. return target
  144. }
  145. // target already has corresponding Proxy
  146. const reactiveFlag = isReadonly
  147. ? ReactiveFlags.READONLY
  148. : ReactiveFlags.REACTIVE
  149. if (hasOwn(target, reactiveFlag)) {
  150. return target[reactiveFlag]
  151. }
  152. // only a whitelist of value types can be observed.
  153. const targetType = getTargetType(target)
  154. if (targetType === TargetType.INVALID) {
  155. return target
  156. }
  157. const observed = new Proxy(
  158. target,
  159. targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  160. )
  161. def(target, reactiveFlag, observed)
  162. return observed
  163. }
  164. export function isReactive(value: unknown): boolean {
  165. if (isReadonly(value)) {
  166. return isReactive((value as Target)[ReactiveFlags.RAW])
  167. }
  168. return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE])
  169. }
  170. export function isReadonly(value: unknown): boolean {
  171. return !!(value && (value as Target)[ReactiveFlags.IS_READONLY])
  172. }
  173. export function isProxy(value: unknown): boolean {
  174. return isReactive(value) || isReadonly(value)
  175. }
  176. export function toRaw<T>(observed: T): T {
  177. return (
  178. (observed && toRaw((observed as Target)[ReactiveFlags.RAW])) || observed
  179. )
  180. }
  181. export function markRaw<T extends object>(value: T): T {
  182. def(value, ReactiveFlags.SKIP, true)
  183. return value
  184. }