【vue高频面试题—基础篇】:vue $nextTick的作用以及原理

51 阅读2分钟
  1. 作用:

$nextTick 的主要作用包括:

  • 确保DOM更新:在数据变化后,如果需要立即访问更新后的DOM,可以使用$nextTick来确保DOM已经完成了更新。

  • 动态调整样式:当数据变化导致DOM结构发生改变时,可能需要在DOM更新后调整元素的样式。

  • 测量尺寸:获取元素的大小或位置信息通常需要在DOM更新后进行,比如滚动到最新位置、计算元素高度等。

  1. 原理:

    • 事件循环和微任务队列机制

      • $nextTick利用了 JavaScript 的事件循环和微任务队列的机制。在 Vue 中,当数据发生变化时,Vue 会把更新 DOM 的操作放在一个微任务队列中。
      • 例如,在 Vue 3 中,$nextTick内部主要是使用Promise.thenPromise的回调函数会被添加到微任务队列)来实现。当调用$nextTick时,它会把传入的回调函数添加到微任务队列的末尾,在当前宏任务(如事件处理函数)中的所有同步代码执行完,并且 DOM 更新的微任务也执行完之后,才会执行$nextTick中的回调函数。
    • 版本差异实现细节(Vue 2 和 Vue 3)

      • Vue 2:在 Vue 2 中,$nextTick的实现会优先使用Promise.then,如果浏览器不支持Promise,则会使用MutationObserver(一种用于监听 DOM 变化的原生 API),再退而求其次会使用setImmediate(一个非标准的 JavaScript API,用于在当前事件循环的下一个迭代中立即执行回调函数)或setTimeout(将回调函数延迟一定时间执行,时间设为 0 也会将其放入宏任务队列)。
      • Vue 3:主要依赖Promise.then来将回调函数放入微任务队列,这样的实现更加简洁和符合现代 JavaScript 的异步处理方式,并且保证了在 DOM 更新后执行回调的准确性。

总结:

Vue 的 $nextTick 用于在 DOM 更新完成后执行回调函数,解决异步更新带来的 DOM 状态滞后问题。它基于 JavaScript 的事件循环机制,优先使用微任务(如 Promise.then),在浏览器不支持时退化为宏任务(如 setTimeout)。

较大概率引出下面的追问:

1. 与 JavaScript 的事件循环相关的问题

可能问题:

  • Vue 的 $nextTick 和 JavaScript 的事件循环有何关系?
  • 为什么 $nextTick 优先使用微任务,而不是直接用宏任务?

回答思路:

  • $nextTick 是基于 JavaScript 的事件循环实现的。

    • 微任务(Promise.then)的优先级比宏任务(setTimeout)高,能更快执行,保证 DOM 更新尽可能早完成。
    • 如果微任务不可用(如老旧浏览器),才会退化为使用宏任务。
  • Vue 中的异步 DOM 更新也是通过事件循环机制实现的,先合并数据更新任务,然后在微任务队列中执行 $nextTick 回调。


2. Vue 的异步 DOM 更新机制

可能问题:

  • 为什么 Vue 要采用异步 DOM 更新?
  • Vue 的异步 DOM 更新机制如何保证性能?

回答思路:

  • 异步更新的原因:Vue 在数据变化后,不会立即触发 DOM 更新,而是将多次数据修改合并为一次更新操作,优化性能并避免重复渲染。

  • 实现机制:Vue 将多次数据变化放入队列中,异步执行 DOM 更新。

    • 一个组件的多次数据变化会被合并(batching updates)。
    • 同时,使用虚拟 DOM 避免直接操作真实 DOM,提升更新性能。

3. $nextTick 的执行顺序问题

可能问题:

  • 如果连续调用多次 $nextTick,回调函数的执行顺序是怎样的?
  • $nextTick 和普通的 setTimeout 谁会先执行?

回答思路:

  1. 连续多次调用 $nextTick:回调函数会按调用顺序依次执行,因为它们都被放入同一个微任务队列中。

  2. $nextTick vs setTimeout

    • $nextTick 使用微任务(Promise.then),微任务优先于宏任务(setTimeout)。
    • 因此,$nextTick 的回调会比 setTimeout 先执行。