剖析vue中的nextTick

简介: 本文从nextTick API 的概念到使用,再到源码,层层剖析。系统地回顾nextTick的相关用法,以及内部调用逻辑。

网络异常,图片无法展示
|


本文从nextTick API 的概念到使用,再到源码,层层剖析。系统地回顾nextTick的相关用法,以及内部调用逻辑。


概念


官方解释:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。


大白话:nextTick是vue中的批量异步更新策略。监听到组件的变化时,不会立即更新,会开启一个队列,同一watcher只入队一次。在下一次事件循环时,刷新队列执行更新。


原理


使用nextTick接收传入的回调函数,将回调函数暂时存放到一个队列中,开启异步更新(调用timerfuc函数)。在timerfuc中会优先使用Promise微任务去执行队列中的所有回调,在下一次事件循环时,更新页面。


在timerfuc中还存在一个降级的过程,是考虑浏览器的兼容设置的。 降级顺序: Promise => MutationObserver => setImmediate => setTimeout

网络异常,图片无法展示
|


可以看到nextTick其实是利用了浏览器的事件循环机制。会将nextTick中的任务存放到微任务队列中去(如果浏览器兼容微任务)。


当函数执行栈为空时,会去判断微任务队列中是否存在微任务,如果存在就会先去执行微任务队列中的任务(可以执行nextTick中的任务),然后再去执行宏任务。

网络异常,图片无法展示
|

关于JS运行机制的细节可以点击 JS运行机制 这篇文章。


如何工作


使用


nextTick会接收两个传入对象,一个是用户的回调,还有一个是上下文对象。在组件内,用户使用的时候,this 已经自动绑定到当前的 Vue 实例上,所有就不需要全局的Vue去调用 Vue.nextTick(cb)


<div id="app">
    <h1>异步更新</h1>
    <p id="p1">{{count}}</p>
  </div>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        count: '原始值'
      },
      mounted() {
        this.count = Math.random()
        console.log('第一次改动值:', this.count)
        this.$nextTick(() => {
          console.log('innerHTML', p1.innerHTML);
        })
      },
    })
  </script>

网络异常,图片无法展示
|

我们可以看到在页面加载之后,count成功获取到了第一次改动的值,并在页面上更新。


源码剖析


那么,页面它是怎么渲染的呢?


老样子,我们通过控制台打断点来看看源码内部到底发生了什么吧 ^_^ !

网络异常,图片无法展示
|


首先从this.count的赋值,因为变量是通过this获取的,并不是使用this内部的$data,所以会走到变量的代理proxy中,然后进入了defineRective中的set拦截。

网络异常,图片无法展示
|


在set拦截中,回去通知dep做更新操作。


网络异常,图片无法展示
|


notify中执行watcher的更新函数update。在此之前,我们可以看出,存在一个watcher的排序,源码中将先创建的更高级的watcher放到前面,先执行。


网络异常,图片无法展示
|


通过遍历执行update,尝试将传入的watcher实例入队,启动异步任务。


网络异常,图片无法展示
|


在nextTick中添加一个flushSchedulerQueue回调。并将其放到callbacks数组中。

网络异常,图片无法展示
|


启动异步任务timerfuc


网络异常,图片无法展示
|


在异步任务中,执行flushCallbacks


网络异常,图片无法展示
|
在其内部就


是遍历执行callbacks数组就相当于在执行之前的flushSchedulerQueue回调。

而在flushSchedulerQueue回调中,是遍历所有的watchers,执行他们的run函数。

网络异常,图片无法展示
|


在watcher的run函数中,会执行自身的get。真正会走到组件的新updateComponent。在updateComponent内部,会先执行render()得到虚拟dom,执行_update()接收vnode,再执行patch(oldvnode,vnode),真实dom变更。



网络异常,图片无法展示
|


大家可以通过看这张nextTick流程图,再配合控制台流程进行解读。


网络异常,图片无法展示
|


应用


1、获取dom更新后最新数值。 根据本文案例,我们将count的修改n遍后,想获取dom更新后最新的count值时:


mounted() {
  this.count = Math.random()
  console.log('第一次改动值:', this.count)
  this.count = Math.random()
  console.log('第二次改动值:', this.count)
  this.count = Math.random()
  console.log('第三次改动值:', this.count)
  this.$nextTick(() => {
    console.log('innerHTML', p1.innerHTML);
  })
},

网络异常,图片无法展示
|


注意点


  • 当我将nextTick放到最前面时,将会获取不到最新值,值会是原始值。


网络异常,图片无法展示
|
这是因为


nextTick时,想要的变量还未进入到异步更新的队列中。在异步更新队列中nextTick先进入,然后才是相关变量进入队列当中,所以nextTick输出的会是原始值。


  • 放到第一次或第二次修改值后面就不会发生变化。

网络异常,图片无法展示
|


这是因为相关watcher只入队一次。在第一次修改值的时候,count变量就会进入异步更新队列中,然后nextTick才会进入。根据队列特性,会先执行count变量的修改,再执行nextTick。所以仍然可以获取到count的最新值。


  • 如果前面有变量修改,nextTick会先于Promise执行。
mounted() {
  this.count = Math.random()
  console.log('第一次改动值:', this.count)
  Promise.resolve().then((res) => {
    console.log('Promise', p1.innerHTML);
  })
  this.$nextTick(() => {
    console.log('innerHTML', p1.innerHTML);
  })
  this.count = Math.random()
  console.log('第二次改动值:', this.count)
  this.count = Math.random()
  console.log('第三次改动值:', this.count)
},


在修改相关值时,已经触发异步更新队列,会将相关变量先存放到队列当中,然后再去执行Promise,将其放入到异步队列中。

网络异常,图片无法展示
|

2、点击获取元素的宽高、滚动位置等


感兴趣的朋友可以关注 Vue源码初识专栏或者关注我哦,会持续输出vue相关知识哦(●'◡'●)。 如果不足,请多指教。

目录
相关文章
|
7天前
|
JavaScript
vue使用iconfont图标
vue使用iconfont图标
55 1
|
18天前
|
JavaScript 关系型数据库 MySQL
基于VUE的校园二手交易平台系统设计与实现毕业设计论文模板
基于Vue的校园二手交易平台是一款专为校园用户设计的在线交易系统,提供简洁高效、安全可靠的二手商品买卖环境。平台利用Vue框架的响应式数据绑定和组件化特性,实现用户友好的界面,方便商品浏览、发布与管理。该系统采用Node.js、MySQL及B/S架构,确保稳定性和多功能模块设计,涵盖管理员和用户功能模块,促进物品循环使用,降低开销,提升环保意识,助力绿色校园文化建设。
|
2月前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
48 1
vue学习第一章
|
2月前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
34 1
|
2月前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
2月前
|
JavaScript 前端开发 开发者
Vue是如何劫持响应式对象的
Vue是如何劫持响应式对象的
35 1
|
2月前
|
JavaScript 前端开发 API
介绍一下Vue中的响应式原理
介绍一下Vue中的响应式原理
37 1
|
2月前
|
JavaScript 前端开发 开发者
vue 数据驱动视图
总之,Vue 数据驱动视图是一种先进的理念和技术,它为前端开发带来了巨大的便利和优势。通过理解和应用这一特性,开发者能够构建出更加动态、高效、用户体验良好的前端应用。在不断发展的前端领域中,数据驱动视图将继续发挥重要作用,推动着应用界面的不断创新和进化。
|
2月前
|
JavaScript 前端开发 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
2月前
|
存储 JavaScript 前端开发
介绍一下Vue的核心功能
介绍一下Vue的核心功能

热门文章

最新文章