refactor(reactivity): refactor iteration key trigger logic + use more robust Map/Set check
This commit is contained in:
parent
cf1b6c666f
commit
0124eacc91
@ -6,7 +6,8 @@ import {
|
|||||||
capitalize,
|
capitalize,
|
||||||
hasOwn,
|
hasOwn,
|
||||||
hasChanged,
|
hasChanged,
|
||||||
toRawType
|
toRawType,
|
||||||
|
isMap
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
|
|
||||||
export type CollectionTypes = IterableCollections | WeakCollections
|
export type CollectionTypes = IterableCollections | WeakCollections
|
||||||
@ -129,7 +130,7 @@ function clear(this: IterableCollections) {
|
|||||||
const target = toRaw(this)
|
const target = toRaw(this)
|
||||||
const hadItems = target.size !== 0
|
const hadItems = target.size !== 0
|
||||||
const oldTarget = __DEV__
|
const oldTarget = __DEV__
|
||||||
? target instanceof Map
|
? isMap(target)
|
||||||
? new Map(target)
|
? new Map(target)
|
||||||
: new Set(target)
|
: new Set(target)
|
||||||
: undefined
|
: undefined
|
||||||
@ -185,9 +186,10 @@ function createIterableMethod(
|
|||||||
): Iterable & Iterator {
|
): Iterable & Iterator {
|
||||||
const target = (this as any)[ReactiveFlags.RAW]
|
const target = (this as any)[ReactiveFlags.RAW]
|
||||||
const rawTarget = toRaw(target)
|
const rawTarget = toRaw(target)
|
||||||
const isMap = rawTarget instanceof Map
|
const targetIsMap = isMap(rawTarget)
|
||||||
const isPair = method === 'entries' || (method === Symbol.iterator && isMap)
|
const isPair =
|
||||||
const isKeyOnly = method === 'keys' && isMap
|
method === 'entries' || (method === Symbol.iterator && targetIsMap)
|
||||||
|
const isKeyOnly = method === 'keys' && targetIsMap
|
||||||
const innerIterator = target[method](...args)
|
const innerIterator = target[method](...args)
|
||||||
const wrap = isReadonly ? toReadonly : isShallow ? toShallow : toReactive
|
const wrap = isReadonly ? toReadonly : isShallow ? toShallow : toReactive
|
||||||
!isReadonly &&
|
!isReadonly &&
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
import { TrackOpTypes, TriggerOpTypes } from './operations'
|
||||||
import { EMPTY_OBJ, isArray, isIntegerKey } from '@vue/shared'
|
import { EMPTY_OBJ, isArray, isIntegerKey, isMap } from '@vue/shared'
|
||||||
|
|
||||||
// The main WeakMap that stores {target -> key -> dep} connections.
|
// The main WeakMap that stores {target -> key -> dep} connections.
|
||||||
// Conceptually, it's easier to think of a dependency as a Dep class
|
// Conceptually, it's easier to think of a dependency as a Dep class
|
||||||
@ -197,20 +197,34 @@ export function trigger(
|
|||||||
if (key !== void 0) {
|
if (key !== void 0) {
|
||||||
add(depsMap.get(key))
|
add(depsMap.get(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// also run for iteration key on ADD | DELETE | Map.SET
|
// also run for iteration key on ADD | DELETE | Map.SET
|
||||||
const shouldTriggerIteration =
|
switch (type) {
|
||||||
(type === TriggerOpTypes.ADD &&
|
case TriggerOpTypes.ADD:
|
||||||
(!isArray(target) || isIntegerKey(key))) ||
|
if (!isArray(target)) {
|
||||||
(type === TriggerOpTypes.DELETE && !isArray(target))
|
add(depsMap.get(ITERATE_KEY))
|
||||||
if (
|
if (isMap(target)) {
|
||||||
shouldTriggerIteration ||
|
|
||||||
(type === TriggerOpTypes.SET && target instanceof Map)
|
|
||||||
) {
|
|
||||||
add(depsMap.get(isArray(target) ? 'length' : ITERATE_KEY))
|
|
||||||
}
|
|
||||||
if (shouldTriggerIteration && target instanceof Map) {
|
|
||||||
add(depsMap.get(MAP_KEY_ITERATE_KEY))
|
add(depsMap.get(MAP_KEY_ITERATE_KEY))
|
||||||
}
|
}
|
||||||
|
} else if (isIntegerKey(key)) {
|
||||||
|
// new index added to array -> length changes
|
||||||
|
add(depsMap.get('length'))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case TriggerOpTypes.DELETE:
|
||||||
|
if (!isArray(target)) {
|
||||||
|
add(depsMap.get(ITERATE_KEY))
|
||||||
|
if (isMap(target)) {
|
||||||
|
add(depsMap.get(MAP_KEY_ITERATE_KEY))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case TriggerOpTypes.SET:
|
||||||
|
if (isMap(target)) {
|
||||||
|
add(depsMap.get(ITERATE_KEY))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const run = (effect: ReactiveEffect) => {
|
const run = (effect: ReactiveEffect) => {
|
||||||
|
@ -16,7 +16,9 @@ import {
|
|||||||
isString,
|
isString,
|
||||||
hasChanged,
|
hasChanged,
|
||||||
NOOP,
|
NOOP,
|
||||||
remove
|
remove,
|
||||||
|
isMap,
|
||||||
|
isSet
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
currentInstance,
|
currentInstance,
|
||||||
@ -335,12 +337,12 @@ function traverse(value: unknown, seen: Set<unknown> = new Set()) {
|
|||||||
for (let i = 0; i < value.length; i++) {
|
for (let i = 0; i < value.length; i++) {
|
||||||
traverse(value[i], seen)
|
traverse(value[i], seen)
|
||||||
}
|
}
|
||||||
} else if (value instanceof Map) {
|
} else if (isMap(value)) {
|
||||||
value.forEach((v, key) => {
|
value.forEach((_, key) => {
|
||||||
// to register mutation dep for existing keys
|
// to register mutation dep for existing keys
|
||||||
traverse(value.get(key), seen)
|
traverse(value.get(key), seen)
|
||||||
})
|
})
|
||||||
} else if (value instanceof Set) {
|
} else if (isSet(value)) {
|
||||||
value.forEach(v => {
|
value.forEach(v => {
|
||||||
traverse(v, seen)
|
traverse(v, seen)
|
||||||
})
|
})
|
||||||
|
@ -58,9 +58,11 @@ export const hasOwn = (
|
|||||||
): key is keyof typeof val => hasOwnProperty.call(val, key)
|
): key is keyof typeof val => hasOwnProperty.call(val, key)
|
||||||
|
|
||||||
export const isArray = Array.isArray
|
export const isArray = Array.isArray
|
||||||
export const isSet = (val: any): boolean => {
|
export const isMap = (val: unknown): val is Map<any, any> =>
|
||||||
return toRawType(val) === 'Set'
|
toTypeString(val) === '[object Map]'
|
||||||
}
|
export const isSet = (val: unknown): val is Set<any> =>
|
||||||
|
toTypeString(val) === '[object Set]'
|
||||||
|
|
||||||
export const isDate = (val: unknown): val is Date => val instanceof Date
|
export const isDate = (val: unknown): val is Date => val instanceof Date
|
||||||
export const isFunction = (val: unknown): val is Function =>
|
export const isFunction = (val: unknown): val is Function =>
|
||||||
typeof val === 'function'
|
typeof val === 'function'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { isArray, isObject, isPlainObject } from './index'
|
import { isArray, isMap, isObject, isPlainObject, isSet } from './index'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For converting {{ interpolation }} values to displayed strings.
|
* For converting {{ interpolation }} values to displayed strings.
|
||||||
@ -13,14 +13,14 @@ export const toDisplayString = (val: unknown): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const replacer = (_key: string, val: any) => {
|
const replacer = (_key: string, val: any) => {
|
||||||
if (val instanceof Map) {
|
if (isMap(val)) {
|
||||||
return {
|
return {
|
||||||
[`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val]) => {
|
[`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val]) => {
|
||||||
;(entries as any)[`${key} =>`] = val
|
;(entries as any)[`${key} =>`] = val
|
||||||
return entries
|
return entries
|
||||||
}, {})
|
}, {})
|
||||||
}
|
}
|
||||||
} else if (val instanceof Set) {
|
} else if (isSet(val)) {
|
||||||
return {
|
return {
|
||||||
[`Set(${val.size})`]: [...val.values()]
|
[`Set(${val.size})`]: [...val.values()]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user