@@ -22,11 +22,17 @@ type MapSources<T> = {
22
22
type FlushMode = 'pre' | 'post' | 'sync' ;
23
23
24
24
interface WatcherOption {
25
- lazy : boolean ;
25
+ lazy : boolean ; // whether or not to delay callcack invoking
26
26
deep : boolean ;
27
27
flush : FlushMode ;
28
28
}
29
29
30
+ export interface VueWatcher {
31
+ lazy : boolean ;
32
+ get ( ) : any ;
33
+ teardown ( ) : void ;
34
+ }
35
+
30
36
let fallbackVM : ComponentInstance ;
31
37
32
38
function flushPreQueue ( this : any ) {
@@ -84,13 +90,39 @@ function queueFlushJob(vm: any, fn: () => void, mode: Exclude<FlushMode, 'sync'>
84
90
}
85
91
}
86
92
93
+ function createVueWatcher (
94
+ vm : ComponentInstance ,
95
+ getter : ( ) => any ,
96
+ callback : ( n : any , o : any ) => any ,
97
+ options : {
98
+ deep : boolean ;
99
+ sync : boolean ;
100
+ immediateInvokeCallback ?: boolean ;
101
+ noRun ?: boolean ;
102
+ before ?: ( ) => void ;
103
+ }
104
+ ) : VueWatcher {
105
+ const index = vm . _watchers . length ;
106
+ // @ts -ignore: use undocumented options
107
+ vm . $watch ( getter , callback , {
108
+ immediate : options . immediateInvokeCallback ,
109
+ deep : options . deep ,
110
+ lazy : options . noRun ,
111
+ sync : options . sync ,
112
+ before : options . before ,
113
+ } ) ;
114
+
115
+ return vm . _watchers [ index ] ;
116
+ }
117
+
87
118
function createWatcher (
88
119
vm : ComponentInstance ,
89
120
source : WatcherSource < unknown > | WatcherSource < unknown > [ ] | SimpleEffect ,
90
121
cb : WatcherCallBack < any > | null ,
91
122
options : WatcherOption
92
123
) : ( ) => void {
93
124
const flushMode = options . flush ;
125
+ const isSync = flushMode === 'sync' ;
94
126
let cleanup : ( ( ) => void ) | null ;
95
127
const registerCleanup : CleanupRegistrator = ( fn : ( ) => void ) => {
96
128
cleanup = ( ) => {
@@ -101,54 +133,51 @@ function createWatcher(
101
133
}
102
134
} ;
103
135
} ;
136
+ // cleanup before running getter again
137
+ const runCleanup = ( ) => {
138
+ if ( cleanup ) {
139
+ cleanup ( ) ;
140
+ cleanup = null ;
141
+ }
142
+ } ;
143
+ const createScheduler = < T extends Function > ( fn : T ) : T => {
144
+ if ( isSync || /* without a current active instance, ignore pre|post mode */ vm === fallbackVM ) {
145
+ return fn ;
146
+ }
147
+ return ( ( ( ...args : any [ ] ) =>
148
+ queueFlushJob (
149
+ vm ,
150
+ ( ) => {
151
+ fn ( ...args ) ;
152
+ } ,
153
+ flushMode as 'pre' | 'post'
154
+ ) ) as any ) as T ;
155
+ } ;
104
156
105
157
// effect watch
106
158
if ( cb === null ) {
107
159
const getter = ( ) => ( source as SimpleEffect ) ( registerCleanup ) ;
108
- // cleanup before running getter again
109
- const runCleanup = ( ) => {
110
- if ( cleanup ) {
111
- cleanup ( ) ;
112
- }
113
- } ;
114
-
115
- if ( flushMode === 'sync' ) {
116
- // @ts -ignore: use undocumented option "sync" ahd "before"
117
- return vm . $watch ( getter , noopFn , {
118
- immediate : true ,
119
- deep : options . deep ,
120
- sync : true ,
121
- before : runCleanup ,
122
- } ) ;
123
- }
124
-
125
- let stopRef : Function ;
126
- let hasEnded : boolean = false ;
127
- const doWatch = ( ) => {
128
- if ( hasEnded ) return ;
160
+ const watcher = createVueWatcher ( vm , getter , noopFn , {
161
+ noRun : true , // take control the initial gettet invoking
162
+ deep : options . deep ,
163
+ sync : isSync ,
164
+ before : runCleanup ,
165
+ } ) ;
129
166
130
- // @ts -ignore: use undocumented option "before"
131
- stopRef = vm . $watch ( getter , noopFn , {
132
- immediate : false ,
133
- deep : options . deep ,
134
- before : runCleanup ,
135
- } ) ;
136
- } ;
167
+ // enable the watcher update
168
+ watcher . lazy = false ;
137
169
138
- /* without a current active instance, ignore pre|post mode */
139
- if ( vm === fallbackVM ) {
140
- vm . $nextTick ( doWatch ) ;
170
+ const originGet = watcher . get . bind ( watcher ) ;
171
+ if ( isSync ) {
172
+ watcher . get ( ) ;
141
173
} else {
142
- queueFlushJob ( vm , doWatch , flushMode ) ;
174
+ vm . $nextTick ( originGet ) ;
143
175
}
176
+ watcher . get = createScheduler ( originGet ) ;
144
177
145
178
return ( ) => {
146
- hasEnded = true ;
147
- if ( stopRef ) {
148
- stopRef ( ) ;
149
- runCleanup ( ) ;
150
- cleanup = null ;
151
- }
179
+ watcher . teardown ( ) ;
180
+ runCleanup ( ) ;
152
181
} ;
153
182
}
154
183
@@ -163,48 +192,33 @@ function createWatcher(
163
192
164
193
const applyCb = ( n : any , o : any ) => {
165
194
// cleanup before running cb again
166
- if ( cleanup ) {
167
- cleanup ( ) ;
168
- }
169
-
195
+ runCleanup ( ) ;
170
196
cb ( n , o , registerCleanup ) ;
171
197
} ;
172
-
173
- const callback =
174
- flushMode === 'sync' ||
175
- /* without a current active instance, ignore pre|post mode */
176
- vm === fallbackVM
177
- ? applyCb
178
- : ( n : any , o : any ) =>
179
- queueFlushJob (
180
- vm ,
181
- ( ) => {
182
- applyCb ( n , o ) ;
183
- } ,
184
- flushMode
185
- ) ;
186
-
187
- // `shiftCallback` is used to handle dirty sync effect.
188
- // The subsequent callbacks will redirect to `callback`.
189
- let shiftCallback = ( n : any , o : any ) => {
190
- shiftCallback = callback ;
191
- applyCb ( n , o ) ;
192
- } ;
198
+ let callback = createScheduler ( applyCb ) ;
199
+ if ( ! options . lazy ) {
200
+ const originalCallbck = callback ;
201
+ // `shiftCallback` is used to handle the first sync effect run.
202
+ // The subsequent callbacks will redirect to `callback`.
203
+ let shiftCallback = ( n : any , o : any ) => {
204
+ shiftCallback = originalCallbck ;
205
+ applyCb ( n , o ) ;
206
+ } ;
207
+ callback = ( n : any , o : any ) => {
208
+ shiftCallback ( n , o ) ;
209
+ } ;
210
+ }
193
211
194
212
// @ts -ignore: use undocumented option "sync"
195
- const stop = vm . $watch ( getter , options . lazy ? callback : shiftCallback , {
213
+ const stop = vm . $watch ( getter , callback , {
196
214
immediate : ! options . lazy ,
197
215
deep : options . deep ,
198
- // @ts -ignore
199
- sync : flushMode === 'sync' ,
216
+ sync : isSync ,
200
217
} ) ;
201
218
202
219
return ( ) => {
203
220
stop ( ) ;
204
- if ( cleanup ) {
205
- cleanup ( ) ;
206
- cleanup = null ;
207
- }
221
+ runCleanup ( ) ;
208
222
} ;
209
223
}
210
224
0 commit comments