Vue实现nextTick

本文详细解析了Vue.js中nextTick的功能与原理,阐述了其在DOM更新中的作用,以及如何确保代码在视图更新后执行。通过具体示例,展示了nextTick在实际开发中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文献
vue nextTick深入理解-vue性能优化、DOM更新时机、事件循环机制
全面解析Vue.nextTick实现原理
这一次,彻底弄懂 JavaScript 执行机制

前言

  • 因为js是单线程运行的,所以在代码执行的过程中,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。
  • 首先在执行同步代码的时候,如果遇到了异步事件(比如访问一个接口),js引擎会将这个异步事件挂起,继续执行执行栈中的下面代码。当异步任务执行完毕后,会将异步事件对应的回调函数(处理后台返回的数据)加入到队列中,这就是为什么异步任务无法保证回调函数的执行顺序,哪个异步任务执行快,将会优先其回调函数放入队列执行,队列又分为微任务队列,宏任务队列。
  • 当当前执行栈中的事件执行完毕后,js引擎首先会判断微任务队列中是否有任务可执行,如果有则将其队首的事件压入执行栈中执行。
  • 执行完微任务队列中的所有任务后即为一个事件循环
  • 接着再去判断宏任务队列中是否有任务,开启新的一个事件循环

主线程的执行过程就是一个tick

一、定义[nextTick、事件循环]

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

nextTick的由来:

由于Vue的数据驱动视图更新是异步的,即修改数据的当下,视图不会立即更新,而是等同一事件循环中的所有数据变化完成之后,再统一进行视图更新。也就是说该方法会将watcher.update()集中在下一个tick进行渲染,这样做的目的是减少性能耗损

nextTick的触发时机:

在同一事件循环中的数据变化后,DOM完成更新,执行nextTick(callback)内的回调。

应用场景:

需要在视图更新之后,基于新的视图进行操作。

Vue.component('example', {
  template: '<span>{{ message }}</span>',
  data: function () {
    return {
      message: '未更新'
    }
  },
  methods: {
    updateMessage: function () {
      this.message = '已更新'
      console.log(this.$el.textContent) // => '未更新'
      this.$nextTick(function () {
        console.log(this.$el.textContent) // => '已更新'
      })
    }
  }
})
  • 在created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。原因是在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
  • mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick 替换掉 mounted:
mounted: function () {
this.$nextTick(function () {

// Code that will run only after the
// entire view has been rendered
})
}

内部实现原理

每一次事件循环都包含一个microtask队列,在循环结束后会依次执行队列中的microtask并移除,然后再开始下一次事件循环。

在执行microtask的过程中后加入microtask队列的微任务,也会在下一次事件循环之前被执行。也就是说,macrotask总要等到microtask都执行完后才能执行,microtask有着更高的优先级。

microtask的这一特性,简直是做队列控制的最佳选择啊!vue进行DOM更新内部也是调用nextTick来做异步队列控制。而当我们自己调用nextTick的时候,它就在更新DOM(异步的)的那个microtask后追加了我们自己的回调函数,从而确保我们的代码在DOM更新后执行

内部实现过程:
  1. 首先想到最优的microtask策略就是Promise,而令人尴尬的是,Promise是ES6新增的东西,也存在兼容问题呀~ 所以vue就面临一个降级策略。降级为macrotask来做队列控制
  2. 接着MutationObserver(也是microtask微任务),但vue在2.5版本中已经删去了MO相关的代码,因为它是HTML5新增的特性,在iOS上尚有bug。
  3. 然后是setImmediate,可惜的是只有IE和nodejs支持
  4. 然后是MessageChannelonmessage回调也是microtask,但也是个新API,面临兼容性的尴尬…
  5. 最后最后的兜底方案就是setTimeout了,尽管它有执行延迟,可能造成多次渲染,算是没有办法的办法了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值