1 |
|
2 |
|
3 | import type Watcher from './watcher'
|
4 | import config from '../config'
|
5 | import { callHook, activateChildComponent } from '../instance/lifecycle'
|
6 |
|
7 | import {
|
8 | warn,
|
9 | nextTick,
|
10 | devtools,
|
11 | inBrowser
|
12 | } from '../util/index'
|
13 |
|
14 | export const MAX_UPDATE_COUNT = 100
|
15 |
|
16 | const queue: Array<Watcher> = []
|
17 | const activatedChildren: Array<Component> = []
|
18 | let has: { [key: number]: ?true } = {}
|
19 | let circular: { [key: number]: number } = {}
|
20 | let waiting = false
|
21 | let flushing = false
|
22 | let index = 0
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | function resetSchedulerState () {
|
28 | index = queue.length = activatedChildren.length = 0
|
29 | has = {}
|
30 | if (process.env.NODE_ENV !== 'production') {
|
31 | circular = {}
|
32 | }
|
33 | waiting = flushing = false
|
34 | }
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | export let currentFlushTimestamp = 0
|
42 |
|
43 |
|
44 | let getNow: () => number = Date.now
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | if (inBrowser && getNow() > document.createEvent('Event').timeStamp) {
|
51 |
|
52 |
|
53 |
|
54 | getNow = () => performance.now()
|
55 | }
|
56 |
|
57 |
|
58 |
|
59 |
|
60 | function flushSchedulerQueue () {
|
61 | currentFlushTimestamp = getNow()
|
62 | flushing = true
|
63 | let watcher, id
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | queue.sort((a, b) => a.id - b.id)
|
74 |
|
75 |
|
76 |
|
77 | for (index = 0; index < queue.length; index++) {
|
78 | watcher = queue[index]
|
79 | if (watcher.before) {
|
80 | watcher.before()
|
81 | }
|
82 | id = watcher.id
|
83 | has[id] = null
|
84 | watcher.run()
|
85 |
|
86 | if (process.env.NODE_ENV !== 'production' && has[id] != null) {
|
87 | circular[id] = (circular[id] || 0) + 1
|
88 | if (circular[id] > MAX_UPDATE_COUNT) {
|
89 | warn(
|
90 | 'You may have an infinite update loop ' + (
|
91 | watcher.user
|
92 | ? `in watcher with expression "${watcher.expression}"`
|
93 | : `in a component render function.`
|
94 | ),
|
95 | watcher.vm
|
96 | )
|
97 | break
|
98 | }
|
99 | }
|
100 | }
|
101 |
|
102 |
|
103 | const activatedQueue = activatedChildren.slice()
|
104 | const updatedQueue = queue.slice()
|
105 |
|
106 | resetSchedulerState()
|
107 |
|
108 |
|
109 | callActivatedHooks(activatedQueue)
|
110 | callUpdatedHooks(updatedQueue)
|
111 |
|
112 |
|
113 |
|
114 | if (devtools && config.devtools) {
|
115 | devtools.emit('flush')
|
116 | }
|
117 | }
|
118 |
|
119 | function callUpdatedHooks (queue) {
|
120 | let i = queue.length
|
121 | while (i--) {
|
122 | const watcher = queue[i]
|
123 | const vm = watcher.vm
|
124 | if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {
|
125 | callHook(vm, 'updated')
|
126 | }
|
127 | }
|
128 | }
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | export function queueActivatedComponent (vm: Component) {
|
135 |
|
136 |
|
137 | vm._inactive = false
|
138 | activatedChildren.push(vm)
|
139 | }
|
140 |
|
141 | function callActivatedHooks (queue) {
|
142 | for (let i = 0; i < queue.length; i++) {
|
143 | queue[i]._inactive = true
|
144 | activateChildComponent(queue[i], true )
|
145 | }
|
146 | }
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 | export function queueWatcher (watcher: Watcher) {
|
154 | const id = watcher.id
|
155 | if (has[id] == null) {
|
156 | has[id] = true
|
157 | if (!flushing) {
|
158 | queue.push(watcher)
|
159 | } else {
|
160 |
|
161 |
|
162 | let i = queue.length - 1
|
163 | while (i > index && queue[i].id > watcher.id) {
|
164 | i--
|
165 | }
|
166 | queue.splice(i + 1, 0, watcher)
|
167 | }
|
168 |
|
169 | if (!waiting) {
|
170 | waiting = true
|
171 |
|
172 | if (process.env.NODE_ENV !== 'production' && !config.async) {
|
173 | flushSchedulerQueue()
|
174 | return
|
175 | }
|
176 | nextTick(flushSchedulerQueue)
|
177 | }
|
178 | }
|
179 | }
|