| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- import { isArray, isIntegerKey, isMap, isSymbol } from '@vue/shared'
- import { type TrackOpTypes, TriggerOpTypes } from './constants'
- import { onTrack, triggerEventInfos } from './debug'
- import {
- type Link,
- ReactiveFlags,
- type ReactiveNode,
- activeSub,
- endBatch,
- link,
- propagate,
- shallowPropagate,
- startBatch,
- } from './system'
- class Dep implements ReactiveNode {
- _subs: Link | undefined = undefined
- subsTail: Link | undefined = undefined
- flags: ReactiveFlags = ReactiveFlags.None
- constructor(
- private map: KeyToDepMap,
- private key: unknown,
- ) {}
- get subs(): Link | undefined {
- return this._subs
- }
- set subs(value: Link | undefined) {
- this._subs = value
- if (value === undefined) {
- this.map.delete(this.key)
- }
- }
- }
- // The main WeakMap that stores {target -> key -> dep} connections.
- // Conceptually, it's easier to think of a dependency as a Dep class
- // which maintains a Set of subscribers, but we simply store them as
- // raw Maps to reduce memory overhead.
- type KeyToDepMap = Map<any, Dep>
- export const targetMap: WeakMap<object, KeyToDepMap> = new WeakMap()
- export const ITERATE_KEY: unique symbol = Symbol(
- __DEV__ ? 'Object iterate' : '',
- )
- export const MAP_KEY_ITERATE_KEY: unique symbol = Symbol(
- __DEV__ ? 'Map keys iterate' : '',
- )
- export const ARRAY_ITERATE_KEY: unique symbol = Symbol(
- __DEV__ ? 'Array iterate' : '',
- )
- /**
- * Tracks access to a reactive property.
- *
- * This will check which effect is running at the moment and record it as dep
- * which records all effects that depend on the reactive property.
- *
- * @param target - Object holding the reactive property.
- * @param type - Defines the type of access to the reactive property.
- * @param key - Identifier of the reactive property to track.
- */
- export function track(target: object, type: TrackOpTypes, key: unknown): void {
- if (activeSub !== undefined) {
- let depsMap = targetMap.get(target)
- if (!depsMap) {
- targetMap.set(target, (depsMap = new Map()))
- }
- let dep = depsMap.get(key)
- if (!dep) {
- depsMap.set(key, (dep = new Dep(depsMap, key)))
- }
- if (__DEV__) {
- onTrack(activeSub!, {
- target,
- type,
- key,
- })
- }
- link(dep, activeSub!)
- }
- }
- /**
- * Finds all deps associated with the target (or a specific property) and
- * triggers the effects stored within.
- *
- * @param target - The reactive object.
- * @param type - Defines the type of the operation that needs to trigger effects.
- * @param key - Can be used to target a specific reactive property in the target object.
- */
- export function trigger(
- target: object,
- type: TriggerOpTypes,
- key?: unknown,
- newValue?: unknown,
- oldValue?: unknown,
- oldTarget?: Map<unknown, unknown> | Set<unknown>,
- ): void {
- const depsMap = targetMap.get(target)
- if (!depsMap) {
- // never been tracked
- return
- }
- const run = (dep: ReactiveNode | undefined) => {
- if (dep !== undefined && dep.subs !== undefined) {
- if (__DEV__) {
- triggerEventInfos.push({
- target,
- type,
- key,
- newValue,
- oldValue,
- oldTarget,
- })
- }
- propagate(dep.subs)
- shallowPropagate(dep.subs)
- if (__DEV__) {
- triggerEventInfos.pop()
- }
- }
- }
- startBatch()
- if (type === TriggerOpTypes.CLEAR) {
- // collection being cleared
- // trigger all effects for target
- depsMap.forEach(run)
- } else {
- const targetIsArray = isArray(target)
- const isArrayIndex = targetIsArray && isIntegerKey(key)
- if (targetIsArray && key === 'length') {
- const newLength = Number(newValue)
- depsMap.forEach((dep, key) => {
- if (
- key === 'length' ||
- key === ARRAY_ITERATE_KEY ||
- (!isSymbol(key) && key >= newLength)
- ) {
- run(dep)
- }
- })
- } else {
- // schedule runs for SET | ADD | DELETE
- if (key !== void 0 || depsMap.has(void 0)) {
- run(depsMap.get(key))
- }
- // schedule ARRAY_ITERATE for any numeric key change (length is handled above)
- if (isArrayIndex) {
- run(depsMap.get(ARRAY_ITERATE_KEY))
- }
- // also run for iteration key on ADD | DELETE | Map.SET
- switch (type) {
- case TriggerOpTypes.ADD:
- if (!targetIsArray) {
- run(depsMap.get(ITERATE_KEY))
- if (isMap(target)) {
- run(depsMap.get(MAP_KEY_ITERATE_KEY))
- }
- } else if (isArrayIndex) {
- // new index added to array -> length changes
- run(depsMap.get('length'))
- }
- break
- case TriggerOpTypes.DELETE:
- if (!targetIsArray) {
- run(depsMap.get(ITERATE_KEY))
- if (isMap(target)) {
- run(depsMap.get(MAP_KEY_ITERATE_KEY))
- }
- }
- break
- case TriggerOpTypes.SET:
- if (isMap(target)) {
- run(depsMap.get(ITERATE_KEY))
- }
- break
- }
- }
- }
- endBatch()
- }
- export function getDepFromReactive(
- object: any,
- key: string | number | symbol,
- ): ReactiveNode | undefined {
- const depMap = targetMap.get(object)
- return depMap && depMap.get(key)
- }
|