全面解析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-ui的项目中封装dialog组件
Dec 11 Vue.js
Vue通过阿里云oss的url连接直接下载文件并修改文件名的方法
Dec 25 Vue.js
vue form表单post请求结合Servlet实现文件上传功能
Jan 22 Vue.js
如何在 Vue 表单中处理图片
Jan 26 Vue.js
vue-router路由懒加载及实现的3种方式
Feb 28 Vue.js
vue 中 get / delete 传递数组参数方法
Mar 23 Vue.js
Vue.js 带下拉选项的输入框(Textbox with Dropdown)组件
Apr 17 Vue.js
Vue通过懒加载提升页面响应速度
May 10 Vue.js
浅谈Vue的computed计算属性
Mar 21 Vue.js
vue项目如何打包之项目打包优化(让打包的js文件变小)
Apr 30 Vue.js
vue实现登陆页面开发实践
May 30 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
PHP3 safe_mode 失效漏洞
2006/10/09 PHP
php生成复杂验证码(倾斜,正弦干扰线,黏贴,旋转)
2018/03/12 PHP
PHP ajax+jQuery 实现批量删除功能实例代码小结
2018/12/06 PHP
利用javascript移动div层-javascript 拖动层
2009/03/22 Javascript
JQuery实现倒计时按钮的实现代码
2012/03/23 Javascript
深入理解javascript学习笔记(一) 编写高质量代码
2012/08/09 Javascript
jQuery筛选器children()案例详解(图文)
2013/02/17 Javascript
利用jQuery的deferred对象实现异步按顺序加载JS文件
2013/03/17 Javascript
JS中如何设置readOnly的值
2013/12/25 Javascript
jQuery使用ajaxSubmit()提交表单示例
2014/04/04 Javascript
js确认删除对话框适用于a标签及submit
2014/07/10 Javascript
js实现宇宙星空背景效果的方法
2015/03/03 Javascript
jquery验证手机号是否正确实例讲解
2015/11/17 Javascript
完美解决spring websocket自动断开连接再创建引发的问题
2017/03/02 Javascript
bootstrap模态框远程示例代码分享
2017/05/22 Javascript
vue路由嵌套的SPA实现步骤
2017/11/06 Javascript
vue解决使用webpack打包后keep-alive不生效的方法
2018/09/01 Javascript
微信小程序公用参数与公用方法用法示例
2019/01/09 Javascript
基于Vue实现平滑过渡的拖拽排序功能
2019/06/12 Javascript
node.js使用yargs处理命令行参数操作示例
2020/02/11 Javascript
vue Treeselect下拉树只能选择第N级元素实现代码
2020/08/31 Javascript
Flask SQLAlchemy一对一,一对多的使用方法实践
2013/02/10 Python
python使用datetime模块计算各种时间间隔的方法
2015/03/24 Python
python基于pyDes库实现des加密的方法
2017/04/29 Python
Python基于最小二乘法实现曲线拟合示例
2018/06/14 Python
python实战串口助手_解决8串口多个发送的问题
2019/06/12 Python
Tensorflow 1.0之后模型文件、权重数值的读取方式
2020/02/12 Python
Keras load_model 导入错误的解决方式
2020/06/09 Python
美国首屈一指的高品质珠宝设计师和零售商:Allurez
2018/01/23 全球购物
广告学专业毕业生自荐信
2013/09/24 职场文书
财务出纳员岗位职责
2013/11/26 职场文书
廉洁家庭事迹材料
2014/05/15 职场文书
汉语言文学专业自荐信
2014/06/11 职场文书
2019交通安全宣传标语集锦!
2019/06/28 职场文书
小学记事作文之200字
2019/08/06 职场文书
分享Python异步爬取知乎热榜
2022/04/12 Python