全面解析Vue中的$nextTick


Posted in Vue.js onDecember 24, 2020

当在代码中更新了数据,并希望等到对应的Dom更新之后,再执行一些逻辑。这时,我们就会用到$nextTick

funcion callback(){
 //等待Dom更新,然后搞点事。
}
$nextTick(callback);

官方文档对nextTick的解释是:

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

那么,Vue是如何做的这一点的,是不是在调用修改Dom的Api之后(appendChild, textContent = "xxxxx" 诸如此类),调用了我们的回调函数?
实际上发生了什么呢。

源码

nextTick的实现逻辑在这个文件里:

vue/src/core/util/next-tick.js

我们调用的this.$nextTick实际上是这个方法:

export function nextTick (cb?: Function, ctx?: Object) {
 let _resolve
 callbacks.push(() => {
  if (cb) {
   try {
    cb.call(ctx)
   } catch (e) {
    handleError(e, ctx, 'nextTick')
   }
  } else if (_resolve) {
   _resolve(ctx)
  }
 })
 if (!pending) {
  pending = true
  timerFunc()
 }
 // $flow-disable-line
 if (!cb && typeof Promise !== 'undefined') {
  return new Promise(resolve => {
   _resolve = resolve
  })
 }
}

可以看到

  1. 回调函数被存放到了一个数组里:callbacks。
  2. 如果没有传递回调函数,这个方法会返回一个Promise,然后吧reslove当成回调函数放到flushCallbacks中。所以文档解释了把本该当成回调函数的callbacks放到then里的用法。
  3. 然后,有一个变量叫pending,如果不在pending中,则执行函数timerFunc。而且pending默认等于false。
  4. flushCallbacks这个函数会一口气执行所有回调函数。

timerFunc

timerFunc定义在这里

可以看到timerFunc是在一个已resolve了的Promise的then 中执行了flushCallbacks.

利用了js事件循环的微任务的机制

所以,每当我们调用$nextTick,如果pending为false,就会调用timerFunc,然后timerFunc会把flushCallbacks给塞到事件循环的队尾,等待被调用。

if (typeof Promise !== 'undefined' && isNative(Promise)) {
 const p = Promise.resolve()
 timerFunc = () => {
  p.then(flushCallbacks)
 }
}

flushCallbacks

然后在这个文件里还有一个函数叫:flushCallbacks
用来把保存的回调函数给全执行并清空。

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

pending

什么时候pending为true呢?

从timerFunc被调用到flushCallbacks被调用期间pending为true

即一个事件循环周期

在pending期间加入的回调函数,会被已经等待执行的flushCallbacks函数给执行。

核心机制

看完源码,发现除了利用了一个微任务的机制,和Dom更新一点关系都没有哇。

其实调用nextTick的不仅是开发者,Vue更新Dom时,也用到了nextTick。

开发者更新绑定的数据之后,Vue就会立刻调用nextTick,把更新Dom的回调函数作为微任务塞到事件循环里去。

于是,在微任务队列中,开发者调用的nextTick的回调函数,就一定在更行Dom的回调函数之后执行了。

但是问题又来了,根据浏览器的渲染机制,渲染线程是在微任务执行完成之后运行的。渲染线程没运行,怎么拿到Dom呢?

因为,渲染线程只是把Dom树渲染成UI而已,Vue更新Dom之后,在Dom树里,新的Dom节点已经存在了,js线程就已经可以拿到新的Dom了。除非开发者读取Dom的计算属性,触发了强制重流渲染线程才会打断js线程。

总结

  1. 首先timerFunc函数负责把回调函数们都丢到事件循环的队尾
  2. 然后,nextTick函数负责把回调函数们都保存起来。
  3. 调用nextTick函数时会调用timerFunc函数
  4. Vue更新Dom也会使用nextTick,而且在开发者调用nextTick之前。
  5. 因为4中的先后关系和事件循环的队列性质,确保了开发者的nextTick的回调一定在Dom更新之后

以上就是解析Vue中的$nextTick的详细内容,更多关于Vue中的$nextTick的资料请关注三水点靠木其它相关文章!

Vue.js 相关文章推荐
vue实现下载文件流完整前后端代码
Nov 17 Vue.js
vue的hash值原理也是table切换实例代码
Dec 14 Vue.js
vue el-upload上传文件的示例代码
Dec 21 Vue.js
全面解析Vue中的$nextTick
Dec 24 Vue.js
vue 实现click同时传入事件对象和自定义参数
Jan 29 Vue.js
如何在vue中使用video.js播放m3u8格式的视频
Feb 01 Vue.js
vue中axios封装使用的完整教程
Mar 03 Vue.js
vue如何批量引入组件、注册和使用详解
May 12 Vue.js
Vue全家桶入门基础教程
May 14 Vue.js
vue项目中的支付功能实现(微信支付和支付宝支付)
Feb 18 Vue.js
VUE递归树形实现多级列表
Jul 15 Vue.js
vue实现登录、注册、退出、跳转等功能
Dec 23 #Vue.js
vue下拉刷新组件的开发及slot的使用详解
Dec 23 #Vue.js
Vue3 实现双盒子定位Overlay的示例
Dec 22 #Vue.js
详解Vue的异步更新实现原理
Dec 22 #Vue.js
Vue组件简易模拟实现购物车
Dec 21 #Vue.js
vue实现购物车的小练习
Dec 21 #Vue.js
Vue实现小购物车功能
Dec 21 #Vue.js
You might like
PHP函数之error_reporting(E_ALL ^ E_NOTICE)详细说明
2011/07/01 PHP
PHP开发中AJAX技术的简单应用
2015/12/11 PHP
Thinkphp 空操作、空控制器、命名空间(详解)
2017/05/05 PHP
yii2的restful api路由实例详解
2019/05/14 PHP
我的javascript 函数链之演变
2011/04/07 Javascript
js常用代码段收集
2011/10/28 Javascript
javascript父、子页面交互技巧总结
2014/08/08 Javascript
用Jquery选择器计算table中的某一列某一行的合计
2014/08/13 Javascript
多个js毫秒倒计时同时进行效果
2016/01/05 Javascript
jQuery使用$.each遍历json数组的简单实现方法
2016/04/18 Javascript
原生JS实现左右箭头选择日期实例代码
2017/03/14 Javascript
用户管理的设计_jquery的ajax实现二级联动效果
2017/07/13 jQuery
使用vue构建移动应用实战代码
2017/08/02 Javascript
如何把vuejs打包出来的文件整合到springboot里
2018/07/26 Javascript
Vue.js中使用iView日期选择器并设置开始时间结束时间校验功能
2018/08/12 Javascript
Node.js开发之套接字(socket)编程入门示例
2019/11/05 Javascript
python进阶教程之异常处理
2014/08/30 Python
简单介绍Python的Tornado框架中的协程异步实现原理
2015/04/23 Python
python executemany的使用及注意事项
2017/03/13 Python
python逐行读写txt文件的实例讲解
2018/04/03 Python
Flask框架Jinjia模板常用语法总结
2018/07/19 Python
Python3 Post登录并且保存cookie登录其他页面的方法
2018/12/28 Python
Python Numpy库datetime类型的处理详解
2019/07/13 Python
python3 字符串知识点学习笔记
2020/02/08 Python
python+gdal+遥感图像拼接(mosaic)的实例
2020/03/10 Python
详解PyQt5中textBrowser显示print语句输出的简单方法
2020/08/07 Python
python3:excel操作之读取数据并返回字典 + 写入的案例
2020/09/01 Python
英国领先的葡萄酒专家:Majestic Wine
2017/05/30 全球购物
internal修饰符起什么作用
2013/12/16 面试题
三好学生自我鉴定
2013/12/17 职场文书
护士思想汇报
2014/01/12 职场文书
税务干部鉴定材料
2014/02/11 职场文书
政法干警核心价值观心得体会
2014/09/11 职场文书
巾帼标兵事迹材料
2014/12/26 职场文书
三好学生个人总结
2015/02/15 职场文书
mysql多表查询-笔记七
2021/04/05 MySQL