写点什么

你不知道的 $nextTick

  • 2021 年 11 月 23 日
  • 本文字数:1612 字

    阅读完需:约 5 分钟

你不知道的$nextTick

当在代码中更新了数据,并希望等到对应的 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 _resolvecallbacks.push(() => {if (cb) {try {cb.call(ctx)} catch (e) {handleError(e, ctx, 'nextTick')}} else if (_resolve) {_resolve(ctx)}})if (!pending) {pending = truetimerFunc()}// $flow-disable-lineif (!cb && typeof Promise !== 'undefined') {return new Promise(resolve => {_resolve = resolve})}}复制代码可以看到


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


可以看到 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 = falseconst copies = callbacks.slice(0)callbacks.length = 0for (let i = 0; i < copies.length; i++) {copiesi}}复制代码 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 线程。


总结首先 timerFunc 函数负责把回调函数们都丢到事件循环的队尾然后,nextTick 函数负责把回调函数们都保存起来。调用 nextTick 函数时会调用 timerFunc 函数 Vue 更新 Dom 也会使用 nextTick,而且在开发者调用 nextTick 之前。因为 4 中的先后关系和事件循环的队列性质,确保了开发者的 nextTick 的回调一定在 Dom 更新之后最后如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163 相互学习,我们会有专业的技术答疑解惑


如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点 star: https://gitee.com/ZhongBangKeJi/CRMEB不胜感激 !

用户头像

还未添加个人签名 2021.11.02 加入

CRMEB就是客户关系管理+营销电商系统实现公众号端、微信小程序端、H5端、APP、PC端用户账号同步,能够快速积累客户、会员数据分析、智能转化客户、有效提高销售、会员维护、网络营销的一款企业应用

评论

发布
暂无评论
你不知道的$nextTick