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)
|
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)', () => {
|
test('observed value should proxy mutations to original (Object)', () => {
|
||||||
const original: any = { foo: 1 }
|
const original: any = { foo: 1 }
|
||||||
const observed = reactive(original)
|
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 {
|
import {
|
||||||
mutableHandlers,
|
mutableHandlers,
|
||||||
readonlyHandlers,
|
readonlyHandlers,
|
||||||
@ -30,17 +30,31 @@ export interface Target {
|
|||||||
[ReactiveFlags.READONLY]?: any
|
[ReactiveFlags.READONLY]?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
const collectionTypes = new Set<Function>([Set, Map, WeakMap, WeakSet])
|
const enum TargetType {
|
||||||
const isObservableType = /*#__PURE__*/ makeMap(
|
INVALID = 0,
|
||||||
'Object,Array,Map,Set,WeakMap,WeakSet'
|
COMMON = 1,
|
||||||
)
|
COLLECTION = 2
|
||||||
|
}
|
||||||
|
|
||||||
const canObserve = (value: Target): boolean => {
|
function targetTypeMap(rawType: string) {
|
||||||
return (
|
switch (rawType) {
|
||||||
!value[ReactiveFlags.SKIP] &&
|
case 'Object':
|
||||||
isObservableType(toRawType(value)) &&
|
case 'Array':
|
||||||
Object.isExtensible(value)
|
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
|
// only unwrap nested ref
|
||||||
@ -148,12 +162,13 @@ function createReactiveObject(
|
|||||||
return target[reactiveFlag]
|
return target[reactiveFlag]
|
||||||
}
|
}
|
||||||
// only a whitelist of value types can be observed.
|
// only a whitelist of value types can be observed.
|
||||||
if (!canObserve(target)) {
|
const targetType = getTargetType(target)
|
||||||
|
if (targetType === TargetType.INVALID) {
|
||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
const observed = new Proxy(
|
const observed = new Proxy(
|
||||||
target,
|
target,
|
||||||
collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
|
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
|
||||||
)
|
)
|
||||||
def(target, reactiveFlag, observed)
|
def(target, reactiveFlag, observed)
|
||||||
return observed
|
return observed
|
||||||
|
Loading…
Reference in New Issue
Block a user