11import { TrackOpTypes , TriggerOpTypes } from './operations'
2- import { EMPTY_OBJ , extend , isArray , isIntegerKey , isMap } from '@vue/shared'
2+ import { extend , isArray , isIntegerKey , isMap } from '@vue/shared'
33
44// The main WeakMap that stores {target -> key -> dep} connections.
55// Conceptually, it's easier to think of a dependency as a Dep class
@@ -9,40 +9,7 @@ type Dep = Set<ReactiveEffect>
99type KeyToDepMap = Map < any , Dep >
1010const targetMap = new WeakMap < any , KeyToDepMap > ( )
1111
12- export interface ReactiveEffect < T = any > {
13- ( ) : T
14- _isEffect : true
15- id : number
16- active : boolean
17- raw : ( ) => T
18- deps : Array < Dep >
19- options : ReactiveEffectOptions
20- allowRecurse : boolean
21- }
22-
23- export interface ReactiveEffectOptions {
24- lazy ?: boolean
25- scheduler ?: ( job : ReactiveEffect ) => void
26- onTrack ?: ( event : DebuggerEvent ) => void
27- onTrigger ?: ( event : DebuggerEvent ) => void
28- onStop ?: ( ) => void
29- /**
30- * Indicates whether the job is allowed to recursively trigger itself when
31- * managed by the scheduler.
32- *
33- * By default, a job cannot trigger itself because some built-in method calls,
34- * e.g. Array.prototype.push actually performs reads as well (#1740) which
35- * can lead to confusing infinite loops.
36- * The allowed cases are component update functions and watch callbacks.
37- * Component update functions may update child component props, which in turn
38- * trigger flush: "pre" watch callbacks that mutates state that the parent
39- * relies on (#1801). Watch callbacks doesn't track its dependencies so if it
40- * triggers itself again, it's likely intentional and it is the user's
41- * responsibility to perform recursive state mutation that eventually
42- * stabilizes (#1727).
43- */
44- allowRecurse ?: boolean
45- }
12+ export type EffectScheduler = ( ) => void
4613
4714export type DebuggerEvent = {
4815 effect : ReactiveEffect
@@ -62,78 +29,100 @@ let activeEffect: ReactiveEffect | undefined
6229
6330export const ITERATE_KEY = Symbol ( __DEV__ ? 'iterate' : '' )
6431export const MAP_KEY_ITERATE_KEY = Symbol ( __DEV__ ? 'Map key iterate' : '' )
32+ export class ReactiveEffect < T = any > {
33+ active = true
34+ deps : Dep [ ] = [ ]
6535
66- export function isEffect ( fn : any ) : fn is ReactiveEffect {
67- return fn && fn . _isEffect === true
68- }
69-
70- export function effect < T = any > (
71- fn : ( ) => T ,
72- options : ReactiveEffectOptions = EMPTY_OBJ
73- ) : ReactiveEffect < T > {
74- if ( isEffect ( fn ) ) {
75- fn = fn . raw
76- }
77- const effect = createReactiveEffect ( fn , options )
78- if ( ! options . lazy ) {
79- effect ( )
80- }
81- return effect
82- }
83-
84- export function stop ( effect : ReactiveEffect ) {
85- if ( effect . active ) {
86- cleanup ( effect )
87- if ( effect . options . onStop ) {
88- effect . options . onStop ( )
89- }
90- effect . active = false
91- }
92- }
36+ // can be attached after creation
37+ onStop ?: ( ) => void
38+ // dev only
39+ onTrack ?: ( event : DebuggerEvent ) => void
40+ // dev only
41+ onTrigger ?: ( event : DebuggerEvent ) => void
9342
94- let uid = 0
43+ constructor (
44+ public fn : ( ) => T ,
45+ public scheduler : EffectScheduler | null = null ,
46+ // allow recursive self-invocation
47+ public allowRecurse = false
48+ ) { }
9549
96- function createReactiveEffect < T = any > (
97- fn : ( ) => T ,
98- options : ReactiveEffectOptions
99- ) : ReactiveEffect < T > {
100- const effect = function reactiveEffect ( ) : unknown {
101- if ( ! effect . active ) {
102- return fn ( )
50+ run ( ) {
51+ if ( ! this . active ) {
52+ return this . fn ( )
10353 }
104- if ( ! effectStack . includes ( effect ) ) {
105- cleanup ( effect )
54+ if ( ! effectStack . includes ( this ) ) {
55+ this . cleanup ( )
10656 try {
10757 enableTracking ( )
108- effectStack . push ( effect )
109- activeEffect = effect
110- return fn ( )
58+ effectStack . push ( ( activeEffect = this ) )
59+ return this . fn ( )
11160 } finally {
11261 effectStack . pop ( )
11362 resetTracking ( )
11463 const n = effectStack . length
11564 activeEffect = n > 0 ? effectStack [ n - 1 ] : undefined
11665 }
11766 }
118- } as ReactiveEffect
119- effect . id = uid ++
120- effect . allowRecurse = ! ! options . allowRecurse
121- effect . _isEffect = true
122- effect . active = true
123- effect . raw = fn
124- effect . deps = [ ]
125- effect . options = options
126- return effect
127- }
67+ }
12868
129- function cleanup ( effect : ReactiveEffect ) {
130- const { deps } = effect
131- if ( deps . length ) {
132- for ( let i = 0 ; i < deps . length ; i ++ ) {
133- deps [ i ] . delete ( effect )
69+ cleanup ( ) {
70+ const { deps } = this
71+ if ( deps . length ) {
72+ for ( let i = 0 ; i < deps . length ; i ++ ) {
73+ deps [ i ] . delete ( this )
74+ }
75+ deps . length = 0
13476 }
135- deps . length = 0
13677 }
78+
79+ stop ( ) {
80+ if ( this . active ) {
81+ this . cleanup ( )
82+ if ( this . onStop ) {
83+ this . onStop ( )
84+ }
85+ this . active = false
86+ }
87+ }
88+ }
89+
90+ export interface ReactiveEffectOptions {
91+ lazy ?: boolean
92+ scheduler ?: EffectScheduler
93+ allowRecurse ?: boolean
94+ onStop ?: ( ) => void
95+ onTrack ?: ( event : DebuggerEvent ) => void
96+ onTrigger ?: ( event : DebuggerEvent ) => void
97+ }
98+
99+ export interface ReactiveEffectRunner < T = any > {
100+ ( ) : T
101+ effect : ReactiveEffect
102+ }
103+
104+ export function effect < T = any > (
105+ fn : ( ) => T ,
106+ options ?: ReactiveEffectOptions
107+ ) : ReactiveEffectRunner {
108+ if ( ( fn as ReactiveEffectRunner ) . effect ) {
109+ fn = ( fn as ReactiveEffectRunner ) . effect . fn
110+ }
111+
112+ const _effect = new ReactiveEffect ( fn )
113+ if ( options ) {
114+ extend ( _effect , options )
115+ }
116+ if ( ! options || ! options . lazy ) {
117+ _effect . run ( )
118+ }
119+ const runner = _effect . run . bind ( _effect ) as ReactiveEffectRunner
120+ runner . effect = _effect
121+ return runner
122+ }
123+
124+ export function stop ( runner : ReactiveEffectRunner ) {
125+ runner . effect . stop ( )
137126}
138127
139128let shouldTrack = true
@@ -185,8 +174,8 @@ export function trackEffects(
185174 if ( ! dep . has ( activeEffect ! ) ) {
186175 dep . add ( activeEffect ! )
187176 activeEffect ! . deps . push ( dep )
188- if ( __DEV__ && activeEffect ! . options . onTrack ) {
189- activeEffect ! . options . onTrack (
177+ if ( __DEV__ && activeEffect ! . onTrack ) {
178+ activeEffect ! . onTrack (
190179 Object . assign (
191180 {
192181 effect : activeEffect !
@@ -284,13 +273,13 @@ export function triggerEffects(
284273 // spread into array for stabilization
285274 for ( const effect of [ ...dep ] ) {
286275 if ( effect !== activeEffect || effect . allowRecurse ) {
287- if ( __DEV__ && effect . options . onTrigger ) {
288- effect . options . onTrigger ( extend ( { effect } , debuggerEventExtraInfo ) )
276+ if ( __DEV__ && effect . onTrigger ) {
277+ effect . onTrigger ( extend ( { effect } , debuggerEventExtraInfo ) )
289278 }
290- if ( effect . options . scheduler ) {
291- effect . options . scheduler ( effect )
279+ if ( effect . scheduler ) {
280+ effect . scheduler ( )
292281 } else {
293- effect ( )
282+ effect . run ( )
294283 }
295284 }
296285 }
0 commit comments