-  Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue刷新队列并执行实际 (已去重的) 工作。Vue在内部对异步队列尝试使用原生的Promise.then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn, 0)代替。
-  宏任务: script,setTimeout,setInterval,postMessage,messageChannel,setImmediate
-  微任务: promise.then,Object.observe,mutationObserver,process.nextTick。
-  例如,当你设置 vm.someData = 'new value',该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的DOM状态来做点什么,这就可能会有些棘手。虽然Vue.js通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触DOM,但是有时我们必须要这么做。为了在数据变化之后等待Vue完成更新DOM,可以在数据变化之后立即使用Vue.nextTick(callback)。这样回调函数将在DOM更新完成后被调用。例如:<div id="example">{{message}}</div> var vm = new Vue({el: '#example',data: {message: '123'} }) vm.message = 'new message' // 更改数据 vm.$el.textContent === 'new message' // false Vue.nextTick(function () {vm.$el.textContent === 'new message' // true })
-  在组件内使用 vm.$nextTick()实例方法特别方便,因为它不需要全局Vue,并且回调函数中的this将自动绑定到当前的Vue实例上:Vue.component('example', {template: '<span>{{ message }}</span>',data: function () {return {message: '未更新'}},methods: {updateMessage: function () {this.message = '已更新'console.log(this.$el.textContent) // => '未更新'this.$nextTick(function () {console.log(this.$el.textContent) // => '已更新'})}} })
-  因为 $nextTick()返回一个Promise对象,所以你可以使用新的ES2017 async/await语法完成相同的事情:methods: {updateMessage: async function () {this.message = '已更新'console.log(this.$el.textContent) // => '未更新'await this.$nextTick()console.log(this.$el.textContent) // => '已更新'} }
-  异步:只要监听到数据变化了,vue就开启一个队列,并缓存在同一个时间循环中发生的所有数据变更。 
-  批量:如果同一个watcher被多次出发,只会被推入到队列中一次。去重对于避免不必要的计算和dom操作非常重要,在下一个时间循环tick中,vue刷新队列执行实际工作 
-  异步策略: vue在内部对异步队列禅师使用原生的Promise.then,mutation observe和setImmediate,如果执行环境不支持,则会采用setTimeout替代。this.foo = 111 this.foo = 222 this.foo = 333 触发foo的dep.notify() notify(){const subs = this.subs.slice()for(let i =0;L = subs.length;i<L;i++){subs[i].update() // 触发update方法,且执行三遍} } class Watcher{update(){queueWatcher(this)} }nextTick function nextTick(cb, ctx) {callbacks.push(() => {cb.call(ctx)})if (!pending) {pending = truePromise.resolve().then(flushCallbacks)} }flushCallbacks function flushCallbacks() {pending = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copies[i]()} }queueWatcher function queueWatcher(watcher) {const id = watcher.idif (has[id] != null) { // 下次有相同id,就不再进入return}if (watcher === Dep.target && watcher.noRecurse) {return}has[id] = trueif (!flushing) {queue.push(watcher)} else {let i = queue.length - 1while (i > index && queue[i].id > watcher.id) {i--}queue.splice(i + 1, 0, watcher)}if (!waiting) {waiting = true// 建立一个微任务,等当前宏任务执行完毕,再执行。nextTick(flushSchedulerQueue)} }flushSchedulerQueue function flushSchedulerQueue() {currentFlushTimestamp = getNow()flushing = truelet watcher, idqueue.sort(sortCompareFn)for (index = 0; index < queue.length; index++) {watcher = queue[index]if (watcher.before) {watcher.before()}id = watcher.idhas[id] = nullwatcher.run()}const activatedQueue = activatedChildren.slice()const updatedQueue = queue.slice()resetSchedulerState()callActivatedHooks(activatedQueue)callUpdatedHooks(updatedQueue)cleanupDeps() }