- vue的双向绑定基于Object.defineProperty以及对于数组原型改变数组自身的7种方法进行增强来实现。
- 在其值发生改变时,通过拦截数据时设置的dep实例调用dep.notify()即循环dep实例的subs数组(一个由wacher所组成的数组),调用每个watcher的update方法
- update将会分作三种情况执行,此处对于lazy为真情况(即computed)以及sync为真(同步直接执行watcher的run进行更新)不做深究
- 在一般情况下,update实际是调用了queueWatcher函数将watcher加入全局的queue数组,通过执行nextTick方法,其回调函数为flushSchedulerQueue(将queue数组进行排序,排序原因源码有注释,并执行watcher的run方法)
- nextTick:将每个flushSchedulerQueue放入全局的callbacks,若此时浏览器的异步任务队列中没有一个叫 flushCallbacks 的函数,则执行 timerFunc 函数,将 flushCallbacks 函数放入异步任务队列。如果异步任务队列中已经存在 flushCallbacks 函数,等待其执行完成以后再放入下一个 flushCallbacks 函数 注意flushCallbacks 是在timerFunc(异步函数)里执行的。 Vue.nextTick也是此方法
- timerFunc:将任务加入异步队列,通过Promise,MutationObserver,setImmediate,setTimeout的优先级进行浏览器兼容处理
- flushCallbacks :依次执行callbacks数组中的方法
- 所以以上简单言之其实就是在异步执行watcher的run方法
if (!flushing) {
queue.push(watcher)
} else {
// 如何出现异步更新已经执行 还有执行queueWatcher的情况
// if already flushing, splice the watcher based on its id 如果已经是flushing状态则将其加入queue
// if already past its id, it will be run next immediately. 如果已经过了它的id,它将被立即运行
let i = queue.length - 1
while (i > index && queue[i].id > watcher.id) {
i--
}
queue.splice(i + 1, 0, watcher)
}
这部分代码始终理解不了,如何出现异步更新已经执行 还有执行queueWatcher的情况,queueWatcher始终是在同步执行,vue也始终是用最快的异步来进行异步更新,是基于未来可能存在的新的异步方案考虑?
如果是一个用户 watcher,即 watch 选项,监听到数据更新,经过异步更新队列的过程,最后开始刷新队列,执行 watcher.run -> watcher.get -> 最后执行 watch 选项的回调函数,如果回调函数中有更新另一个响应式数据的情况,这时候就会触发 setter,然后触发依赖通知更新,接着 watcher 入队。