fix(reactivity): accept subtypes of collections (#1864)
This commit is contained in:
parent
6ccd9ac2bc
commit
d005b578b1
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user