baseHandlers.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import { reactive, readonly, toRaw } from './reactive'
  2. import { OperationTypes } from './operations'
  3. import { track, trigger } from './effect'
  4. import { LOCKED } from './lock'
  5. import { isObject, hasOwn, isSymbol } from '@vue/shared'
  6. import { isRef } from './ref'
  7. const builtInSymbols = new Set(
  8. Object.getOwnPropertyNames(Symbol)
  9. .map(key => (Symbol as any)[key])
  10. .filter(isSymbol)
  11. )
  12. function createGetter(isReadonly: boolean) {
  13. return function get(target: object, key: string | symbol, receiver: object) {
  14. const res = Reflect.get(target, key, receiver)
  15. if (isSymbol(key) && builtInSymbols.has(key)) {
  16. return res
  17. }
  18. if (isRef(res)) {
  19. return res.value
  20. }
  21. track(target, OperationTypes.GET, key)
  22. return isObject(res)
  23. ? isReadonly
  24. ? // need to lazy access readonly and reactive here to avoid
  25. // circular dependency
  26. readonly(res)
  27. : reactive(res)
  28. : res
  29. }
  30. }
  31. function set(
  32. target: object,
  33. key: string | symbol,
  34. value: unknown,
  35. receiver: object
  36. ): boolean {
  37. value = toRaw(value)
  38. const oldValue = (target as any)[key]
  39. if (isRef(oldValue) && !isRef(value)) {
  40. oldValue.value = value
  41. return true
  42. }
  43. const hadKey = hasOwn(target, key)
  44. const result = Reflect.set(target, key, value, receiver)
  45. // don't trigger if target is something up in the prototype chain of original
  46. if (target === toRaw(receiver)) {
  47. /* istanbul ignore else */
  48. if (__DEV__) {
  49. const extraInfo = { oldValue, newValue: value }
  50. if (!hadKey) {
  51. trigger(target, OperationTypes.ADD, key, extraInfo)
  52. } else if (value !== oldValue) {
  53. trigger(target, OperationTypes.SET, key, extraInfo)
  54. }
  55. } else {
  56. if (!hadKey) {
  57. trigger(target, OperationTypes.ADD, key)
  58. } else if (value !== oldValue) {
  59. trigger(target, OperationTypes.SET, key)
  60. }
  61. }
  62. }
  63. return result
  64. }
  65. function deleteProperty(target: object, key: string | symbol): boolean {
  66. const hadKey = hasOwn(target, key)
  67. const oldValue = (target as any)[key]
  68. const result = Reflect.deleteProperty(target, key)
  69. if (result && hadKey) {
  70. /* istanbul ignore else */
  71. if (__DEV__) {
  72. trigger(target, OperationTypes.DELETE, key, { oldValue })
  73. } else {
  74. trigger(target, OperationTypes.DELETE, key)
  75. }
  76. }
  77. return result
  78. }
  79. function has(target: object, key: string | symbol): boolean {
  80. const result = Reflect.has(target, key)
  81. track(target, OperationTypes.HAS, key)
  82. return result
  83. }
  84. function ownKeys(target: object): (string | number | symbol)[] {
  85. track(target, OperationTypes.ITERATE)
  86. return Reflect.ownKeys(target)
  87. }
  88. export const mutableHandlers: ProxyHandler<object> = {
  89. get: createGetter(false),
  90. set,
  91. deleteProperty,
  92. has,
  93. ownKeys
  94. }
  95. export const readonlyHandlers: ProxyHandler<object> = {
  96. get: createGetter(true),
  97. set(
  98. target: object,
  99. key: string | symbol,
  100. value: unknown,
  101. receiver: object
  102. ): boolean {
  103. if (LOCKED) {
  104. if (__DEV__) {
  105. console.warn(
  106. `Set operation on key "${String(key)}" failed: target is readonly.`,
  107. target
  108. )
  109. }
  110. return true
  111. } else {
  112. return set(target, key, value, receiver)
  113. }
  114. },
  115. deleteProperty(target: object, key: string | symbol): boolean {
  116. if (LOCKED) {
  117. if (__DEV__) {
  118. console.warn(
  119. `Delete operation on key "${String(
  120. key
  121. )}" failed: target is readonly.`,
  122. target
  123. )
  124. }
  125. return true
  126. } else {
  127. return deleteProperty(target, key)
  128. }
  129. },
  130. has,
  131. ownKeys
  132. }