这应该是最详细的响应式系统讲解了(3)

Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue刷新队列并执行实际 (已去重的) 工作。Vue在内部对异步队列尝试使用原生的Promise.then、MutationObserver和setImmediate,如果执行环境不支持,则会采用setTimeout(fn, 0)代替。

根据以上这段官方文档,这个队列主要是异步和去重,首先我们来整理一下思路:

需要有一个队列来存储一个事件循环中的数据变更,且要对它去重。

将当前事件循环中的数据变更添加到队列。

异步的去执行这个队列中的所有数据变更。

// 使用Set数据结构创建一个队列,这样可自动去重 let queue = new Set() // 在属性出发set方法时会触发watcher.update,继而执行以下方法 function pushQueue (watcher) { // 将数据变更添加到队列 queue.add(watcher) // 下一个tick执行该数据变更,所以nextTick接受的应该是一个能执行queue队列的函数 nextTick('一个能遍历执行queue的函数') } // 用Promise模拟nextTick function nextTick('一个能遍历执行queue的函数') { Promise.resolve().then('一个能遍历执行queue的函数') }

以上已经有个大体的思路了,那接下来完成'一个能遍历执行queue的函数':

// queue是一个数组,所以直接遍历执行即可 function flushQueue () { queue.forEach(watcher => { // 触发watcher中的run方法进行真正的更新操作 watcher.run() }) // 执行后清空队列 queue = new Set() }

还有一个问题,那就是同一个事件循环中应该只要触发一次nextTick即可,而不是每次添加队列时都触发:

// 设置一个是否触发了nextTick的标识 let waiting = false function pushQueue (watcher) { queue.add(watcher) if (!waiting) { // 保证nextTick只触发一次 waiting = true nextTick('一个能遍历执行queue的函数') } }

完整代码如下:

// 定义队列 let queue = new Set() // 供传入nextTick中的执行队列的函数 function flushQueue () { queue.forEach(watcher => { watcher.run() }) queue = new Set() } // nextTick function nextTick(flushQueue) { Promise.resolve().then(flushQueue) } // 添加到队列并调用nextTick let waiting = false function pushQueue (watcher) { queue.add(watcher) if (!waiting) { waiting = true nextTick(flushQueue) } }

最后

以上就是响应式的一个大概原理,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/727c87d83321a7d285e0d88bc5525955.html