@@ -119,6 +119,161 @@ export function defineReactive (
119
119
})
120
120
}
121
121
` ` `
122
+ ` ` ` javascript
123
+ const queue: Array <Watcher > = []
124
+ let has: { [key: number]: ? true } = {}
125
+ let waiting = false
126
+ let flushing = false
127
+ /**
128
+ * Push a watcher into the watcher queue.
129
+ * Jobs with duplicate IDs will be skipped unless it's
130
+ * pushed when the queue is being flushed.
131
+ */
132
+ export function queueWatcher (watcher : Watcher ) {
133
+ const id = watcher .id
134
+ if (has[id] == null ) {
135
+ has[id] = true
136
+ if (! flushing) {
137
+ queue .push (watcher)
138
+ } else {
139
+ // if already flushing, splice the watcher based on its id
140
+ // if already past its id, it will be run next immediately.
141
+ let i = queue .length - 1
142
+ while (i > index && queue[i].id > watcher .id ) {
143
+ i--
144
+ }
145
+ queue .splice (i + 1 , 0 , watcher)
146
+ }
147
+ // queue the flush
148
+ if (! waiting) {
149
+ waiting = true
150
+ nextTick (flushSchedulerQueue)
151
+ }
152
+ }
153
+ }
154
+ ` ` `
155
+ 每次添加watcher时检查是否存在,只加入不存在的watcher
156
+
157
+ ` ` ` javascript
158
+ import { noop } from ' shared/util'
159
+ import { handleError } from ' ./error'
160
+ import { isIOS , isNative } from ' ./env'
161
+
162
+ const callbacks = []
163
+ let pending = false
164
+
165
+ function flushCallbacks () {
166
+ pending = false
167
+ const copies = callbacks .slice (0 )
168
+ callbacks .length = 0
169
+ for (let i = 0 ; i < copies .length ; i++ ) {
170
+ copies[i]()
171
+ }
172
+ }
173
+
174
+ // Here we have async deferring wrappers using both microtasks and (macro) tasks.
175
+ // In < 2.4 we used microtasks everywhere, but there are some scenarios where
176
+ // microtasks have too high a priority and fire in between supposedly
177
+ // sequential events (e.g. #4521, #6690) or even between bubbling of the same
178
+ // event (#6566). However, using (macro) tasks everywhere also has subtle problems
179
+ // when state is changed right before repaint (e.g. #6813, out-in transitions).
180
+ // Here we use microtask by default, but expose a way to force (macro) task when
181
+ // needed (e.g. in event handlers attached by v-on).
182
+ let microTimerFunc
183
+ let macroTimerFunc
184
+ let useMacroTask = false
185
+
186
+ // Determine (macro) task defer implementation.
187
+ // Technically setImmediate should be the ideal choice, but it's only available
188
+ // in IE. The only polyfill that consistently queues the callback after all DOM
189
+ // events triggered in the same loop is by using MessageChannel.
190
+ /* istanbul ignore if */
191
+ if (typeof setImmediate !== ' undefined' && isNative (setImmediate)) {
192
+ macroTimerFunc = () => {
193
+ setImmediate (flushCallbacks)
194
+ }
195
+ } else if (typeof MessageChannel !== ' undefined' && (
196
+ isNative (MessageChannel ) ||
197
+ // PhantomJS
198
+ MessageChannel .toString () === ' [object MessageChannelConstructor]'
199
+ )) {
200
+ const channel = new MessageChannel ()
201
+ const port = channel .port2
202
+ channel .port1 .onmessage = flushCallbacks
203
+ macroTimerFunc = () => {
204
+ port .postMessage (1 )
205
+ }
206
+ } else {
207
+ /* istanbul ignore next */
208
+ macroTimerFunc = () => {
209
+ setTimeout (flushCallbacks, 0 )
210
+ }
211
+ }
212
+
213
+ // Determine microtask defer implementation.
214
+ /* istanbul ignore next, $flow-disable-line */
215
+ if (typeof Promise !== ' undefined' && isNative (Promise )) {
216
+ const p = Promise .resolve ()
217
+ microTimerFunc = () => {
218
+ p .then (flushCallbacks)
219
+ // in problematic UIWebViews, Promise.then doesn't completely break, but
220
+ // it can get stuck in a weird state where callbacks are pushed into the
221
+ // microtask queue but the queue isn't being flushed, until the browser
222
+ // needs to do some other work, e.g. handle a timer. Therefore we can
223
+ // "force" the microtask queue to be flushed by adding an empty timer.
224
+ if (isIOS) setTimeout (noop)
225
+ }
226
+ } else {
227
+ // fallback to macro
228
+ microTimerFunc = macroTimerFunc
229
+ }
230
+
231
+ /**
232
+ * Wrap a function so that if any code inside triggers state change,
233
+ * the changes are queued using a (macro) task instead of a microtask.
234
+ */
235
+ export function withMacroTask (fn : Function ): Function {
236
+ return fn ._withTask || (fn ._withTask = function () {
237
+ useMacroTask = true
238
+ const res = fn .apply (null , arguments )
239
+ useMacroTask = false
240
+ return res
241
+ })
242
+ }
243
+
244
+ export function nextTick (cb ?: Function , ctx ?: Object ) {
245
+ let _resolve
246
+ callbacks .push (() => {
247
+ if (cb) {
248
+ try {
249
+ cb .call (ctx)
250
+ } catch (e) {
251
+ handleError (e, ctx, ' nextTick' )
252
+ }
253
+ } else if (_resolve) {
254
+ _resolve (ctx)
255
+ }
256
+ })
257
+ if (! pending) {
258
+ pending = true
259
+ if (useMacroTask) {
260
+ macroTimerFunc ()
261
+ } else {
262
+ microTimerFunc ()
263
+ }
264
+ }
265
+ // $flow-disable-line
266
+ if (! cb && typeof Promise !== ' undefined' ) {
267
+ return new Promise (resolve => {
268
+ _resolve = resolve
269
+ })
270
+ }
271
+ }
272
+ ` ` `
273
+ IOS下有一个bug,如果当前没有执行的 task 那么不会执行microtask
274
+
275
+
276
+ ## 数据的初始化
122
277
* 根据 vm.$options.data 选项获取真正想要的数据(注意:此时 vm.$options.data 是函数)
123
278
* 校验得到的数据是否是一个纯对象
124
279
* 检查数据对象 data 上的键是否与 props 对象上的键冲突
0 commit comments