Przeglądaj źródła

fix(reactivity): accept subtypes of collections (#1864)

ᴜɴвʏтᴇ 5 lat temu
rodzic
commit
d005b578b1

+ 18 - 0
packages/reactivity/__tests__/reactive.spec.ts

@@ -44,6 +44,24 @@ describe('reactivity/reactive', () => {
     expect(isReactive(observed.array[0])).toBe(true)
   })
 
+  test('process subtypes of collections properly', () => {
+    class CustomMap extends Map {
+      count = 0
+
+      set(key: any, value: any): this {
+        super.set(key, value)
+        this.count++
+        return this
+      }
+    }
+
+    const testMap = new CustomMap()
+    const observed = reactive(testMap)
+    expect(observed.count).toBe(0)
+    observed.set('test', 'value')
+    expect(observed.count).toBe(1)
+  })
+
   test('observed value should proxy mutations to original (Object)', () => {
     const original: any = { foo: 1 }
     const observed = reactive(original)

+ 28 - 13
packages/reactivity/src/reactive.ts

@@ -1,4 +1,4 @@
-import { isObject, toRawType, def, hasOwn, makeMap } from '@vue/shared'
+import { isObject, toRawType, def, hasOwn } from '@vue/shared'
 import {
   mutableHandlers,
   readonlyHandlers,
@@ -30,17 +30,31 @@ export interface Target {
   [ReactiveFlags.READONLY]?: any
 }
 
-const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
-const isObservableType = /*#__PURE__*/ makeMap(
-  'Object,Array,Map,Set,WeakMap,WeakSet'
-)
+const enum TargetType {
+  INVALID = 0,
+  COMMON = 1,
+  COLLECTION = 2
+}
 
-const canObserve = (value: Target): boolean => {
-  return (
-    !value[ReactiveFlags.SKIP] &&
-    isObservableType(toRawType(value)) &&
-    Object.isExtensible(value)
-  )
+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))
 }
 
 // only unwrap nested ref
@@ -148,12 +162,13 @@ function createReactiveObject(
     return target[reactiveFlag]
   }
   // only a whitelist of value types can be observed.
-  if (!canObserve(target)) {
+  const targetType = getTargetType(target)
+  if (targetType === TargetType.INVALID) {
     return target
   }
   const observed = new Proxy(
     target,
-    collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
+    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
   )
   def(target, reactiveFlag, observed)
   return observed