• 异步:只要侦听到数据变化,Vue 将开启⼀个队列,并缓冲在同⼀事件循环中发⽣的所有数据变更。
    • 批量:如果同⼀个 watcher 被多次触发,只会被推⼊到队列中⼀次。去重对于避免不必要的计算 和 DOM 操作是⾮常重要的。然后,在下⼀个的事件循环“tick”中,Vue 刷新队列执⾏实际⼯作。
    • 异步策略:Vue 在内部对异步队列尝试使⽤原⽣的 Promise.thenMutationObserversetImmediate,如果执⾏环境都不⽀持,则会采⽤ setTimeout代替。

    image.png
    core\observer\watcher.js
    dep.notify()之后watcher执⾏更新,执⾏⼊队操作

    1. update () {
    2. /* istanbul ignore else */
    3. // computed
    4. if (this.lazy) {
    5. this.dirty = true
    6. } else if (this.sync) {
    7. this.run()
    8. } else {
    9. // 入队
    10. queueWatcher(this)
    11. }
    12. }

    core\observer\scheduler.js
    执⾏watcher⼊队操作

    1. export function queueWatcher (watcher: Watcher) {
    2. // 去重
    3. const id = watcher.id
    4. // 不存在才入队
    5. if (has[id] == null) {
    6. has[id] = true
    7. if (!flushing) {
    8. queue.push(watcher)
    9. } else {
    10. // if already flushing, splice the watcher based on its id
    11. // if already past its id, it will be run next immediately.
    12. let i = queue.length - 1
    13. while (i > index && queue[i].id > watcher.id) {
    14. i--
    15. }
    16. queue.splice(i + 1, 0, watcher)
    17. }
    18. // queue the flush
    19. if (!waiting) {
    20. waiting = true
    21. if (process.env.NODE_ENV !== 'production' && !config.async) {
    22. flushSchedulerQueue()
    23. return
    24. }
    25. // 异步执行flushSchedulerQueue
    26. nextTick(flushSchedulerQueue)
    27. }
    28. }
    29. }

    core\util\next-tick.js
    nextTick按照特定异步策略执⾏队列操作

    就是Vue.nextTick $nextTick把传入回调函数放入 callback 队尾

    1. // 回调函数数组
    2. const callbacks = []
    3. let pending = false
    4. // 刷新回调函数数组
    5. function flushCallbacks () {
    6. pending = false
    7. const copies = callbacks.slice(0)
    8. callbacks.length = 0
    9. // 遍历并执行
    10. for (let i = 0; i < copies.length; i++) {
    11. copies[i]()
    12. }
    13. }
    14. // 将cb函数放入回调队列队尾
    15. export function nextTick (cb?: Function, ctx?: Object) {
    16. let _resolve
    17. callbacks.push(() => {
    18. if (cb) {
    19. try {
    20. cb.call(ctx)
    21. } catch (e) {
    22. handleError(e, ctx, 'nextTick')
    23. }
    24. } else if (_resolve) {
    25. _resolve(ctx)
    26. }
    27. })
    28. if (!pending) {
    29. pending = true
    30. // 异步执行函数
    31. timerFunc()
    32. }
    33. // $flow-disable-line
    34. if (!cb && typeof Promise !== 'undefined') {
    35. return new Promise(resolve => {
    36. _resolve = resolve
    37. })
    38. }
    39. }

    timerFunc()

    1. if (typeof Promise !== 'undefined' && isNative(Promise)) {
    2. //首选Promise
    3. const p = Promise.resolve()
    4. timerFunc = () => {
    5. p.then(flushCallbacks)
    6. ...
    7. }

    参考: http://www.booleanln.top/2019/09/14/nextTick%E6%B5%85%E6%9E%90/ https://ppt.baomitu.com/d/0052678d https://juejin.cn/post/6930413268376748045 https://github.com/ziyi2/ziyi2.github.io/issues/5

    nextTick - 图2

    image.png