全面解析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添加自定义右键菜单的完整实例
Dec 08 Vue.js
vue使用exif获取图片经纬度的示例代码
Dec 11 Vue.js
vue 动态创建组件的两种方法
Dec 31 Vue.js
Vue中引入svg图标的两种方式
Jan 14 Vue.js
详解vue之自行实现派发与广播(dispatch与broadcast)
Jan 19 Vue.js
用vite搭建vue3应用的实现方法
Feb 22 Vue.js
如何使用vue3打造一个物料库
May 08 Vue.js
Vue接口封装的完整步骤记录
May 14 Vue.js
Vue + iView实现Excel上传功能的完整代码
Jun 22 Vue.js
浅谈Vue的computed计算属性
Mar 21 Vue.js
vue3.0 数字翻牌组件的使用方法详解
Apr 20 Vue.js
vue router 动态路由清除方式
May 25 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
使用sockets:从新闻组中获取文章(三)
2006/10/09 PHP
PHP无敌近乎加密方式!
2010/07/17 PHP
php对称加密算法示例
2014/05/07 PHP
变量在 PHP7 内部的实现(一)
2015/12/21 PHP
深入剖析PHP中printf()函数格式化使用
2016/05/23 PHP
Yii2中DropDownList简单用法示例
2016/07/18 PHP
PHP实现的同步推荐操作API接口案例分析
2016/11/30 PHP
yii2.0框架使用 beforeAction 防非法登陆的方法分析
2019/09/11 PHP
Laravel Eloquent分表方法并使用模型关联的实现
2019/11/25 PHP
JavaScript判断DOM何时加载完毕的技巧
2012/11/11 Javascript
简介JavaScript中Math.LOG10E属性的使用
2015/06/14 Javascript
Angularjs中如何使用filterFilter函数过滤
2016/02/06 Javascript
利用js获取下拉框中所选的值
2016/12/01 Javascript
在 Linux/Unix 中不重启 Vim 而重新加载 .vimrc 文件的流程
2018/03/21 Javascript
微信小程序dom操作的替代思路实例分析
2018/12/06 Javascript
小程序组件之自定义顶部导航实例
2019/06/12 Javascript
vue 翻页组件vue-flip-page效果
2020/02/05 Javascript
JS实现canvas简单小画板功能
2020/06/23 Javascript
vue中activated的用法
2021/01/03 Vue.js
Angular处理未可知异常错误的方法详解
2021/01/17 Javascript
[00:32]10月24、25日 辉夜杯外卡赛附加赛开赛!
2015/10/23 DOTA
Python线程详解
2015/06/24 Python
python hook监听事件详解
2018/10/25 Python
Python绘制堆叠柱状图的实例
2019/07/09 Python
Python高级特性 切片 迭代解析
2019/08/23 Python
如何基于python实现归一化处理
2020/01/20 Python
pandas之分组groupby()的使用整理与总结
2020/06/18 Python
心得体会开头
2014/01/01 职场文书
农村婚礼证婚词
2014/01/10 职场文书
学校消防安全制度
2014/01/30 职场文书
三八妇女节活动主持词
2014/03/17 职场文书
2014副局长群众路线对照检查材料思想汇报
2014/09/22 职场文书
2016自主招生教师推荐信范文
2015/03/23 职场文书
十二月早安励志心语大全
2019/12/03 职场文书
vue-cropper插件实现图片截取上传组件封装
2021/05/27 Vue.js
MySQL中存储时间的最佳实践指南
2021/07/01 MySQL