]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(reactivity): accept subtypes of collections (#1864)
authorᴜɴвʏтᴇ <i@shangyes.net>
Mon, 17 Aug 2020 16:17:46 +0000 (00:17 +0800)
committerGitHub <noreply@github.com>
Mon, 17 Aug 2020 16:17:46 +0000 (12:17 -0400)
packages/reactivity/__tests__/reactive.spec.ts
packages/reactivity/src/reactive.ts

index b7f9cdf4eb3ae3c8ff15ea32c7b6151f55dcf16d..ab798ff2c8b0a6aa510ba943f1e89d6d6f3c7286 100644 (file)
@@ -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)
index 7541886c405a6e69d3cae1490d8a0489b40b80ba..a6602db2c5c5e74feef3dc2bac7443eb998556eb 100644 (file)
@@ -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