Vue-$nextTrick

56 阅读2分钟

1. 核心原理

  • 基于 EventLoop$nextTick 利用 JavaScript 的 异步任务队列 机制(微任务/宏任务)实现。
  • 异步队列优先级
    • 优先使用 微任务PromiseMutationObserver)。
    • 降级使用 宏任务setImmediatesetTimeout)。
  • 设计目的:将多次数据变更触发的 DOM 更新 合并到一次异步队列中执行,避免频繁的同步渲染。

2. 核心作用

  • 确保在 DOM 更新后执行代码:Vue 的 DOM 更新是 异步的,数据变化后不会立即更新视图。$nextTick 的回调会在 DOM 更新完成后触发
  • 应用场景
    1. 操作更新后的 DOM:例如获取元素尺寸、焦点等。
    2. created 生命周期操作 DOM:此时 DOM 尚未渲染完成。
    3. 依赖更新后 DOM 的逻辑:例如数据变化后,需要基于新 DOM 进行二次计算。

3. 代码示例

// 修改数据后立即获取更新后的 DOM
this.message = "更新后的内容";
this.$nextTick(() => {
  console.log(this.$refs.element.textContent); // 输出:更新后的内容
});

// 在 created 中操作 DOM
created() {
  this.$nextTick(() => {
    this.$refs.button.focus(); // DOM 渲染完成后执行
  });
}

4. 实现机制(简化版)

  1. Vue 在数据变化时,不会立即更新 DOM,而是将需要更新的 Watcher 推入一个队列。
  2. 通过 $nextTick 将回调函数推入一个 异步队列
  3. 同一事件循环 中,先执行数据更新队列,再执行 $nextTick 回调队列。
  4. 使用微任务/宏任务保证回调在 DOM 更新后执行。

5. 关键面试问题

Q1: 为什么 Vue 采用异步更新队列?

  • 性能优化:合并多次数据变更,避免频繁的 DOM 渲染。
  • 避免无效计算:例如连续修改多个数据时,只需计算一次最终 DOM。

Q2: $nextTicksetTimeout(fn, 0) 的区别?

  • 执行时机$nextTick 优先使用微任务(更早执行),setTimeout 是宏任务(较晚执行)。
  • 可靠性$nextTick 能确保在 Vue 的 DOM 更新后触发,而 setTimeout 可能因其他任务延迟。

Q3: 在 Vue3 中 nextTick 有何变化?

  • 用法一致,但通过 Composition API 调用:
    import { nextTick } from 'vue';
    nextTick(() => { /* 逻辑 */ });
    

6. 注意事项

  • 避免滥用:仅在需要操作更新后 DOM 时使用。
  • 异步特性:回调函数中的代码是异步执行的。
  • 兼容性:Vue 内部已处理了不同环境的降级策略,无需开发者关心。

记忆技巧

  • 一句话总结$nextTick 是 Vue 提供的 “DOM 更新完成通知器”,利用 JavaScript 异步机制保证回调在 DOM 更新后执行。
  • 类比:数据变化后,Vue 将 DOM 更新任务放入“任务队列”,$nextTick 将你的代码“插队”到队列末尾,确保你的代码在 DOM 更新后运行。

回答:vue的dom更新是异步的,数据在发生变化后是不会立即更新视图的。$nextTick的回调函数会在DOM更新完成后触发。

有一个很典型的应用场景就是有一个编辑按钮,点编辑按钮会出现一个输入框。要求实现焦点的获取。

第一行代码是v-show或者v-if值的改变显示输入框,第二行代码是获取输入框焦点,this.$ref.edit输入框元素.focus()此时执行代码是不会出现输入框焦点获取的。

就像刚才说的dom更新是异步的,当数据发生变化后是不会立即更新视图的,更获取不到输入框元素进行获取焦点了。这样设计的好处是提升性能,如果发生一个变化就立即更新视图这样开销会很大。

此时要想点击编辑按钮去进行输入框焦点的获取,就得使用nextrick,他会等视图更新完成后,执行里面的回调函数,再去获取输入框元素,进行焦点的获取。