Skip to content

Commit

Permalink
refactor: move code to reactiveEffect.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsoncodehk committed Oct 18, 2023
1 parent 93b5beb commit fc76e81
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 152 deletions.
8 changes: 2 additions & 6 deletions packages/reactivity/__tests__/effect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ import {
readonly,
ReactiveEffectRunner
} from '../src/index'
import {
ITERATE_KEY,
getDepFromReactive,
pauseScheduling,
resetScheduling
} from '../src/effect'
import { pauseScheduling, resetScheduling } from '../src/effect'
import { ITERATE_KEY, getDepFromReactive } from '../src/reactiveEffect'

describe('reactivity/effect', () => {
it('should run the passed function once (wrapped by a effect)', () => {
Expand Down
4 changes: 1 addition & 3 deletions packages/reactivity/src/baseHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import {
} from './reactive'
import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
import {
track,
trigger,
ITERATE_KEY,
pauseTracking,
resetTracking,
pauseScheduling,
resetScheduling
} from './effect'
import { track, trigger, ITERATE_KEY } from './reactiveEffect'
import {
isObject,
hasOwn,
Expand Down
7 changes: 6 additions & 1 deletion packages/reactivity/src/collectionHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { toRaw, toReactive, toReadonly } from './reactive'
import { track, trigger, ITERATE_KEY, MAP_KEY_ITERATE_KEY } from './effect'
import {
track,
trigger,
ITERATE_KEY,
MAP_KEY_ITERATE_KEY
} from './reactiveEffect'
import { ReactiveFlags, TrackOpTypes, TriggerOpTypes } from './constants'
import { capitalize, hasOwn, hasChanged, toRawType, isMap } from '@vue/shared'

Expand Down
141 changes: 3 additions & 138 deletions packages/reactivity/src/effect.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { extend } from '@vue/shared'
import type { ComputedRefImpl } from './computed'
import { DirtyLevels, TrackOpTypes, TriggerOpTypes } from './constants'
import { extend, isArray, isIntegerKey, isMap } from '@vue/shared'
import type { Dep } from './dep'
import { EffectScope, recordEffectScope } from './effectScope'
import { createDep, Dep } from './dep'
import type { ComputedRefImpl } from './computed'

// 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 Sets to reduce memory overhead.
type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<object, KeyToDepMap>()

export type EffectScheduler = (
onScheduled: (cb: () => void) => void,
Expand All @@ -31,9 +24,6 @@ export type DebuggerEventExtraInfo = {

export let activeEffect: ReactiveEffect | undefined

export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '')

export class ReactiveEffect<T = any> {
active = true
deps: Dep[] = []
Expand Down Expand Up @@ -260,38 +250,6 @@ export function resetScheduling() {
}
}

/**
* 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) {
if (shouldTrack && activeEffect) {
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = createDep(() => depsMap!.delete(key))))
}
if (__DEV__) {
trackEffect(activeEffect, dep, {
target,
type,
key
})
} else {
trackEffect(activeEffect, dep)
}
}
}

export function trackEffect(
effect: ReactiveEffect,
dep: Dep,
Expand All @@ -314,95 +272,6 @@ export function trackEffect(
}
}

/**
* 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>
) {
const depsMap = targetMap.get(target)
if (!depsMap) {
// never been tracked
return
}

let deps: (Dep | undefined)[] = []
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
deps = [...depsMap.values()]
} else if (key === 'length' && isArray(target)) {
const newLength = Number(newValue)
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= newLength) {
deps.push(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
if (key !== void 0) {
deps.push(depsMap.get(key))
}

// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// new index added to array -> length changes
deps.push(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
deps.push(depsMap.get(ITERATE_KEY))
}
break
}
}

pauseScheduling()
for (const dep of deps) {
if (dep) {
if (__DEV__) {
triggerEffects(dep, DirtyLevels.Dirty, {
target,
type,
key,
newValue,
oldValue,
oldTarget
})
} else {
triggerEffects(dep, DirtyLevels.Dirty)
}
}
}
resetScheduling()
}

const queueEffectCbs: (() => void)[] = []
const pushEffectCb = queueEffectCbs.push.bind(queueEffectCbs)

Expand Down Expand Up @@ -436,7 +305,3 @@ export function triggerEffects(
}
resetScheduling()
}

export function getDepFromReactive(object: any, key: string | number | symbol) {
return targetMap.get(object)?.get(key)
}
4 changes: 1 addition & 3 deletions packages/reactivity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,11 @@ export { deferredComputed } from './deferredComputed'
export {
effect,
stop,
trigger,
track,
enableTracking,
pauseTracking,
resetTracking,
pauseScheduling,
resetScheduling,
ITERATE_KEY,
ReactiveEffect,
type ReactiveEffectRunner,
type ReactiveEffectOptions,
Expand All @@ -64,6 +61,7 @@ export {
type DebuggerEvent,
type DebuggerEventExtraInfo
} from './effect'
export { trigger, track, ITERATE_KEY } from './reactiveEffect'
export {
effectScope,
EffectScope,
Expand Down
146 changes: 146 additions & 0 deletions packages/reactivity/src/reactiveEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { isArray, isIntegerKey, isMap } from '@vue/shared'
import { DirtyLevels, TrackOpTypes, TriggerOpTypes } from './constants'
import { createDep, Dep } from './dep'
import {
activeEffect,
pauseScheduling,
resetScheduling,
shouldTrack,
trackEffect,
triggerEffects
} from './effect'

// 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 Sets to reduce memory overhead.
type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<object, KeyToDepMap>()

export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key 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) {
if (shouldTrack && activeEffect) {
let depsMap = targetMap.get(target)
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
depsMap.set(key, (dep = createDep(() => depsMap!.delete(key))))
}
if (__DEV__) {
trackEffect(activeEffect, dep, {
target,
type,
key
})
} else {
trackEffect(activeEffect, dep)
}
}
}

/**
* 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>
) {
const depsMap = targetMap.get(target)
if (!depsMap) {
// never been tracked
return
}

let deps: (Dep | undefined)[] = []
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
deps = [...depsMap.values()]
} else if (key === 'length' && isArray(target)) {
const newLength = Number(newValue)
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= newLength) {
deps.push(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
if (key !== void 0) {
deps.push(depsMap.get(key))
}

// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// new index added to array -> length changes
deps.push(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
deps.push(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
deps.push(depsMap.get(ITERATE_KEY))
}
break
}
}

pauseScheduling()
for (const dep of deps) {
if (dep) {
if (__DEV__) {
triggerEffects(dep, DirtyLevels.Dirty, {
target,
type,
key,
newValue,
oldValue,
oldTarget
})
} else {
triggerEffects(dep, DirtyLevels.Dirty)
}
}
}
resetScheduling()
}

export function getDepFromReactive(object: any, key: string | number | symbol) {
return targetMap.get(object)?.get(key)
}
2 changes: 1 addition & 1 deletion packages/reactivity/src/ref.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
activeEffect,
getDepFromReactive,
shouldTrack,
trackEffect,
triggerEffects
Expand All @@ -19,6 +18,7 @@ import type { ShallowReactiveMarker } from './reactive'
import { CollectionTypes } from './collectionHandlers'
import { createDep, Dep } from './dep'
import { ComputedRefImpl } from './computed'
import { getDepFromReactive } from './reactiveEffect'

declare const RefSymbol: unique symbol
export declare const RawSymbol: unique symbol
Expand Down

0 comments on commit fc76e81

Please sign in to comment.