全面解析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使用Element实现增删改查+打包的步骤
Nov 25 Vue.js
vue表单验证之禁止input输入框输入空格
Dec 03 Vue.js
vue从后台渲染文章列表以及根据id跳转文章详情详解
Dec 14 Vue.js
vue实现简易的双向数据绑定
Dec 29 Vue.js
SpringBoot+Vue 前后端合并部署的配置方法
Dec 30 Vue.js
Vue实现图书管理案例
Jan 20 Vue.js
vue3.0封装轮播图组件的步骤
Mar 04 Vue.js
Vue和Flask通信的实现
May 19 Vue.js
vue实现同时设置多个倒计时
May 20 Vue.js
Vue3.0 手写放大镜效果
Jul 25 Vue.js
Vue OpenLayer 为地图绘制风场效果
Apr 24 Vue.js
vue实现在data里引入相对路径
Jun 05 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 pack与unpack 摸板字符字符含义
2009/10/29 PHP
PHP 万年历实现代码
2012/10/18 PHP
php微信公众号开发(4)php实现自定义关键字回复
2016/12/15 PHP
PHP中的自动加载操作实现方法详解
2019/08/06 PHP
为你的 Laravel 验证器加上多验证场景的实现
2020/04/07 PHP
javascript 解析后的xml对象的读取方法细解
2009/07/25 Javascript
THREE.JS入门教程(1)THREE.JS使用前了解
2013/01/24 Javascript
javascript计算用户打开网页的停留时间
2014/01/09 Javascript
fixedBox固定div漂浮代码支持ie6以上大部分主流浏览器
2014/06/26 Javascript
深入理解JavaScript系列(26):设计模式之构造函数模式详解
2015/03/03 Javascript
jQuery选择器源码解读(四):tokenize方法的Expr.preFilter
2015/03/31 Javascript
JavaScript操作URL的相关内容集锦
2015/10/29 Javascript
使用微信内置浏览器点击下拉框出现页面乱跳转现象(iphone),该怎么办
2016/01/04 Javascript
JavaScript中ES6字符串扩展方法
2016/08/26 Javascript
AngularJs  unit-testing(单元测试)详解
2016/09/02 Javascript
Js操作DOM元素及获取浏览器高宽的简单方法
2016/09/08 Javascript
js实现随机抽选效果、随机抽选红色球效果
2017/01/13 Javascript
MUI实现上拉加载和下拉刷新效果
2017/06/30 Javascript
浅谈通过JS拦截 pushState和replaceState事件
2017/07/21 Javascript
使用ngrok+express解决本地环境中微信接口调试问题
2018/02/26 Javascript
VUE 实现滚动监听 导航栏置顶的方法
2018/09/11 Javascript
react antd表格中渲染一张或多张图片的实例
2020/10/28 Javascript
[58:57]2018DOTA2亚洲邀请赛3月29日小组赛B组 Effect VS VGJ.T
2018/03/30 DOTA
对Python强大的可变参数传递机制详解
2019/06/13 Python
Python实现代码统计工具
2019/09/19 Python
详解如何在PyCharm控制台中输出彩色文字和背景
2020/08/17 Python
Pycharm制作搞怪弹窗的实现代码
2021/02/19 Python
html5 localStorage本地存储_动力节点Java学院整理
2017/07/06 HTML / CSS
HTML5 canvas基本绘图之填充样式实现
2016/06/27 HTML / CSS
澳大利亚Rockwear官网:女子瑜伽、健身和运动服
2021/01/26 全球购物
.NET里面如何取得当前的屏幕分辨率
2012/12/06 面试题
营销专业应届生求职信
2013/11/26 职场文书
个人作风剖析材料
2014/02/02 职场文书
党员三严三实心得体会
2014/10/13 职场文书
土地租赁协议书
2015/01/29 职场文书
2015大学迎新晚会主持词
2015/07/16 职场文书