2022-09-16Vue00
请注意,本文编写于 667 天前,最后修改于 667 天前,其中某些信息可能已经过时。

nextTick实现原理

此题属于原理题目,能够体现面试者对vue理解深度,答好了会加分很多。

答题思路:

  1. 此题实际考查vue异步更新策略
  2. 说出vue是怎么通过异步、批量的方式更新以提高性能的
  3. 最后把源码中实现说一下

回答范例:

  1. vue有个批量、异步更新策略,数据变化时,vue开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。然后在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

    官方文档在这里

  2. 源码中,修改一个数据,组件对应的watcher会尝试入队:

queue.push(watcher)

并使用nextTick方法添加一个flushSchedulerQueue回调

nextTick(flushSchedulerQueue)

flushSchedulerQueue被加入callbacks数组

callbacks.push(() => {
  if (cb) {
    try {
      cb.call(ctx) // cb就是加入的回调
    } catch (e) {
      handleError(e, ctx, 'nextTick')
    }
  } 
})

然后以异步方式启动

if (!pending) {
  pending = true
  timerFunc()
}

timerFunc的异步主要利用Promise等微任务方式实现

let timerFunc

if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  // timerFunc利用p.then向微任务队列添加一个flushCallbacks
  // 会异步调用flushCallbacks
  timerFunc = () => {
    p.then(flushCallbacks)
  }
  isUsingMicroTask = true
}

flushCallbacks遍历callbacks,执行里面所有回调

function flushCallbacks () {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

其中就有前面加入的flushSchedulerQueue,它主要用于执行queue中所有watcher的run方法,从而使组件们更新

for (index = 0; index < queue.length; index++) {
  watcher = queue[index]
  watcher.run()
}

可能的追问

你平时什么时候会用到nextTick?

本文作者:毛超颖

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!