reactive.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { isObject } from '@vue/shared'
  2. import { mutableHandlers, immutableHandlers } from './baseHandlers'
  3. import {
  4. mutableCollectionHandlers,
  5. immutableCollectionHandlers
  6. } from './collectionHandlers'
  7. import { UnwrapRef } from './ref'
  8. import { ReactiveEffect } from './effect'
  9. // The main WeakMap that stores {target -> key -> dep} connections.
  10. // Conceptually, it's easier to think of a dependency as a Dep class
  11. // which maintains a Set of subscribers, but we simply store them as
  12. // raw Sets to reduce memory overhead.
  13. export type Dep = Set<ReactiveEffect>
  14. export type KeyToDepMap = Map<string | symbol, Dep>
  15. export const targetMap: WeakMap<any, KeyToDepMap> = new WeakMap()
  16. // WeakMaps that store {raw <-> observed} pairs.
  17. const rawToObserved: WeakMap<any, any> = new WeakMap()
  18. const observedToRaw: WeakMap<any, any> = new WeakMap()
  19. const rawToImmutable: WeakMap<any, any> = new WeakMap()
  20. const immutableToRaw: WeakMap<any, any> = new WeakMap()
  21. // WeakSets for values that are marked immutable or non-reactive during
  22. // observable creation.
  23. const immutableValues: WeakSet<any> = new WeakSet()
  24. const nonReactiveValues: WeakSet<any> = new WeakSet()
  25. const collectionTypes: Set<any> = new Set([Set, Map, WeakMap, WeakSet])
  26. const observableValueRE = /^\[object (?:Object|Array|Map|Set|WeakMap|WeakSet)\]$/
  27. const canObserve = (value: any): boolean => {
  28. return (
  29. !value._isVue &&
  30. !value._isVNode &&
  31. observableValueRE.test(Object.prototype.toString.call(value)) &&
  32. !nonReactiveValues.has(value)
  33. )
  34. }
  35. type ObservableFactory = <T>(target?: T) => UnwrapRef<T>
  36. export const reactive = ((target: unknown): any => {
  37. // if trying to observe an immutable proxy, return the immutable version.
  38. if (immutableToRaw.has(target)) {
  39. return target
  40. }
  41. // target is explicitly marked as immutable by user
  42. if (immutableValues.has(target)) {
  43. return immutable(target)
  44. }
  45. return createObservable(
  46. target,
  47. rawToObserved,
  48. observedToRaw,
  49. mutableHandlers,
  50. mutableCollectionHandlers
  51. )
  52. }) as ObservableFactory
  53. export const immutable = ((target: unknown): any => {
  54. // value is a mutable observable, retrive its original and return
  55. // a readonly version.
  56. if (observedToRaw.has(target)) {
  57. target = observedToRaw.get(target)
  58. }
  59. return createObservable(
  60. target,
  61. rawToImmutable,
  62. immutableToRaw,
  63. immutableHandlers,
  64. immutableCollectionHandlers
  65. )
  66. }) as ObservableFactory
  67. function createObservable(
  68. target: any,
  69. toProxy: WeakMap<any, any>,
  70. toRaw: WeakMap<any, any>,
  71. baseHandlers: ProxyHandler<any>,
  72. collectionHandlers: ProxyHandler<any>
  73. ) {
  74. if (!isObject(target)) {
  75. if (__DEV__) {
  76. console.warn(`value is not observable: ${String(target)}`)
  77. }
  78. return target
  79. }
  80. // target already has corresponding Proxy
  81. let observed = toProxy.get(target)
  82. if (observed !== void 0) {
  83. return observed
  84. }
  85. // target is already a Proxy
  86. if (toRaw.has(target)) {
  87. return target
  88. }
  89. // only a whitelist of value types can be observed.
  90. if (!canObserve(target)) {
  91. return target
  92. }
  93. const handlers = collectionTypes.has(target.constructor)
  94. ? collectionHandlers
  95. : baseHandlers
  96. observed = new Proxy(target, handlers)
  97. toProxy.set(target, observed)
  98. toRaw.set(observed, target)
  99. if (!targetMap.has(target)) {
  100. targetMap.set(target, new Map())
  101. }
  102. return observed
  103. }
  104. export function isReactive(value: any): boolean {
  105. return observedToRaw.has(value) || immutableToRaw.has(value)
  106. }
  107. export function isImmutable(value: any): boolean {
  108. return immutableToRaw.has(value)
  109. }
  110. export function toRaw<T>(observed: T): T {
  111. return observedToRaw.get(observed) || immutableToRaw.get(observed) || observed
  112. }
  113. export function markImmutable<T>(value: T): T {
  114. immutableValues.add(value)
  115. return value
  116. }
  117. export function markNonReactive<T>(value: T): T {
  118. nonReactiveValues.add(value)
  119. return value
  120. }