首页 > 其他分享 >Vue $nextTick原理

Vue $nextTick原理

时间:2023-11-17 16:11:27浏览次数:36  
标签:nextTick Vue 函数 DOM 任务 原理 执行

image

作用:vue 更新 DOM 是异步更新的,数据变化,DOM 的更新不会马上完成,nextTick 的回调是在下次 DOM 更新循环结束之后执行的延迟回调。

实现原理:nextTick 主要使用了宏任务和微任务。根据执行环境分别尝试采用
Promise:可以将函数延迟到当前函数调用栈最末端
MutationObserver:是 H5 新加的一个功能,其功能是监听 DOM 节点的变动,在所有 DOM 变动完成后,执行回调函数
setImmediate:用于中断长时间运行的操作,并在浏览器完成其他操作(如事件和显示更新)后立即运行回调函数
如果以上都不行则采用 setTimeout 把函数延迟到 DOM 更新之后再使用,原因是宏任务消耗大于微任务,优先使用微任务,最后使用消耗最大的宏任务。

vue2.5 以前的版本:nextTick 实质是产生一个回调函数加入到 task(宏任务)或者 microtask(微任务),当前栈执行完后调用该回调函数,起到了异步触发的作用

vue2.5 以后全部使用微任务实现,原因是使用宏任务会产生一些问题
由于浏览器的事件循环机制,引擎在每一个宏任务执行完毕,从队列中取下一个宏任务执行之前,会将这个宏任务下的微任务队列拿出来依次执行,因此微任务的执行时间早于宏任务。每个 task 执行完后都会触发 UI 的重新渲染,在 microTask 中完成数据更新,当前 task 结束就能拿到最新的 UI 了,如果再新建一个 task,UI 渲染就会进行两次

总结一下它的流程就是

把回调函数放入 callbacks 等待执行
将执行函数放到微任务或者宏任务中
事件循环到了微任务或者宏任务,执行函数依次执行 callbacks 中的回调

再回到开头说的 setTimeout,可以看出来 nextTick 是对 setTimeout 进行了多种兼容性的处理,宽泛的也可以理解为将回调函数放入 setTimeout 中执行;不过 nextTick 优先放入微任务执行,而 setTimeout 是宏任务,因此 nextTick 一般情况下总是先于 setTimeout 执行,可以在浏览器中尝试一下

最后验证猜想;当前宏任务执行完成后;优先执行两个微任务;最后再执行宏任务。

this.$nextTick()使用情景:

在 Vue 生命周期的 created 和 mounted 钩子函数进行的 DOM 操作一定要放在 Vue.nextTick()的回调函数中。
原因:是 created()钩子函数执行时 DOM 其实并未进行渲染。

在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的 DOM 结构的时候,这个操作应该放在 Vue.nextTick()的回调函数中。
原因:Vue 异步执行 DOM 更新,只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,如果同一个 watcher 被多次触发,只会被推入到队列中一次。

先看下官方文档的说明:

Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。

nextTick 就是将回调函数放到队列里去,保证在异步更新 DOM 的 watcher 后面,从而获取到更新后的 DOM。
结合 src/core/util/next-tick 源码再进行分析。
首先是定义执行任务队列方法

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

按照推入 callbacks 队列的顺序执行回调函数。
然后定义 timerFunc 函数,根据当前环境支持什么方法来确定调用哪个异步方法
判断的顺序是: Promise > MutationObserver > setImmediate > setTimeout
最后是定义 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();
  }
  if (!cb && typeof Promise !== "undefined") {
    return new Promise((resolve) => {
      _resolve = resolve;
    });
  }
}

其实 nextTick 就是一个把回调函数推入任务队列的方法。
了解到这里也差不多了,再深入的话可以说 vue 中数据变化,触发 watcher,watcher 进入队列的流程,可以另一篇文章(https://juejin.cn/post/6934539800527503368)

标签:nextTick,Vue,函数,DOM,任务,原理,执行
From: https://www.cnblogs.com/wp-leonard/p/17839005.html

相关文章

  • vm.$set原理
    给对应和数组本身都增加了dep属性当给对象新增不存在的属性则触发对象依赖的watcher去更新当修改数组索引时,调用数组本身的splice去更新数组(数组的响应式原理就是重新了splice等方法,调用splice就会触发视图更新)splice();push();pop();shift();unshift();sort(......
  • vue2为什么只重写了7个数组方法
    首先看源码//即将要被劫持的数组letarr=[1,2,3];//先把要劫持的方法列出来letmethods=["push","pop","shift","unshift","reverse","sort","splice"];//既然要劫持原型,就要先把原型拿过来letarrayProto=Array.prototy......
  • vue组件递归
    这样的场景:渲染列表数据的时候,列表的子项还是列表。如果层级少尚且可以用几个for循环搞定,但是层级多或者层级不确定就有点无从下手了。其实这就是树形结构数据,像常见的组织架构图,文件夹目录,导航菜单等都属于这种结构。很多组件库都带有树形组件,但往往样式不是想要的,改起来也非......
  • vue2.0源码简读(5. 扩展)
    5.1event平时开发工作中,处理组件间的通讯,原生的交互,都离不开事件。对于一个组件元素,不仅仅可以绑定原生的DOM事件,还可以绑定自定义事件,非常灵活和方便。那么接下来从源码角度来看看它的实现原理。为了更加直观,通过一个例子来分析它的实现:letChild={template:'<button......
  • vue2.0源码简读(6. Vue Router)
    6.1路由注册Vue从它的设计上就是一个渐进式JavaScript框架,它本身的核心是解决视图渲染的问题,其它的能力就通过插件的方式来解决。Vue-Router就是官方维护的路由插件,在介绍它的注册实现之前,先来分析一下Vue通用的插件注册原理。Vue.useVue提供了Vue.use的全局API来......
  • vue2.0源码简读(7. Vuex)
    7.1Vuex初始化这一节主要来分析Vuex的初始化过程,它包括安装、Store实例化过程2个方面。安装当在代码中通过importVuexfrom'vuex'的时候,实际上引用的是一个对象,它的定义在src/index.js中:exportdefault{Store,install,version:"__VERSION__",mapSt......
  • vue中created、watch和computed的执行顺序
    总结关于vue中created和watch的执行顺序相对比较简单,而其中computed是通过Object.defineProperty为当前vm进行定义,再到后续创建vNode阶段才去触发执行其get函数,最终执行到计算属性computed对应的逻辑。官网的生命周期图中,initreactivity是晚于beforeCreate......
  • 硬件开发笔记(十二):RK3568底板电路电源模块和RTC模块原理图分析
    前言  做硬件做系统做驱动,很难从核心板做起,所以我们先依赖核心板,分析底板周围的电路,然后使用AD绘制原理图和设计PCB,打样我司测试底板,完成硬件测试,再继续系统适配,驱动移植,从而一步一步完善成为一个功能完善的底板,且搭载了我们跳完的系统和驱动。  本篇文章,先从底板的电源电......
  • 深度学习算法原理实现——模型欠拟合和过拟合处理
    欠拟合:fromtensorflow.kerasimportregularizersimportnumpyasnpfromtensorflowimportkerasfromtensorflow.kerasimportlayersfromtensorflow.keras.datasetsimportimdbfromtensorflow.keras.datasetsimportmnistdefplot_val_loss_and_acc(model):......
  • 深度学习算法原理实现——线性分类器
     importnumpyasnpimporttensorflowastfimportmatplotlib.pyplotaspltdefmodel(inputs):returntf.matmul(inputs,W)+bdefsquare_loss(targets,predictions):per_sample_losses=tf.square(targets-predictions)returntf.reduce_mean......