nextTick()

113 阅读5分钟

以下内容源对大佬的学习整理,有问题欢迎指出,谢谢~

在理解nextTick之前,需要理解一下js事件循环

1、js运行机制

1.1同步任务和异步任务

单线程语言出现的问题--假死

image.png 解决程序假死问题

image.png

image.png 3-5步骤 js主线程从任务队列中读取异步任务的回调函数,放到执行栈中依次执行,这个过程是循环不断的,就叫事件循环

1.2宏任务task和微任务job

一、js宏任务和微任务分别有哪些? 1、js宏任务有:setTimeout、setInterval、setImmediate、Ajax、DOM事件

2、js微任务有:process.nextTick、MutationObserver、Promise.then catch finally

3、 new Promise(…)是构造函数,是同步代码。 执行顺序---交替执行:

同步——>异步——>微任务——>宏任务。JS是单线程,碰见同步执行同步直到执行完毕,遇到异步放到执行队列中去,异步包括宏任务和微任务,在异步中微任务是优于宏任务执行的。如果宏任务里包括微任务,那就可以看作宏任务的执行顺序先于微任务。

一个例子:

image.png 输出顺序:

setTimeout就是作为宏任务来存在的,而Promise.then则是具有代表性的微任务

1、整个这一串代码我们所在的层级我们看做一个任务,其中我们先执行同步代码。第一行的 setTimeout 是异步代码,跳过,来到了 new Promise(…) 这一段代码。

2、前面提到过,这种方式是一个构造函数,是一个同步代码,所以执行同步代码里面的函数,即 console.log(1),接下来是一个 then 的异步,跳过。最后一个是一段同步代码 console.log(2)。所以,这一轮中我们知道打印了1, 2两个值。接下来进入下一步,即之前我们跳过的异步的代码。从上往下,第一个是 setTimeout,还有一个是Promise.then()。

3、setTimeout 是宏任务的异步,Promise.then()是微任务的异步,微任务是优先于宏任务执行的,所以,此时会先跳过 setTimeout 任务,执行两个 Promise.then() 的微任务。所以此时会执行 console.log(3) 函数。最后就只剩下 setTimeout 函数没有执行,所以最后执行 console.log(4)。

4、综上,最后的执行结果是:1, 2, 3, 4

1.3async/await (重点)

async/await 底层依然是 Promise,所以是微任务,只是 await 比较特殊

await表示等待,是右侧「表达式」的结果,这个表达式的计算结果可以是 Promise 对象的值或者一个函数的值(换句话说,就是没有特殊限定)。并且只能在带有async的内部使用

使用await时,会从右往左执行,当遇到await时, ★★★★★会阻塞函数内部处于它后面的代码(比如下面的执行完async2,就阻止后面的console.log( 'async1 end' ),会去继续执行console.log( 'promise1' )),去执行该函数外部的同步代码,当外部同步代码执行完毕,再回到该函数内部执行剩余的代码★★★★★, 并且当await执行完毕之后,会先处理微任务队列的代码

2、强大的异步专家 process.nextTick()

process.nextTick() 可以确保 API 始终是异步的,即使它不必是

通过将回调置于process.nextTick()中,那么当前宏任务中的同步脚本先运行完成,在事件循环之前 (即执行下一个tick--宏任务前) 执行这个回调。因此在调用回调之前就能拿到所有的变量、函数等。

process.nextTick()还具有阻止事件循环继续的优势,适用于在事件循环继续之前,提醒用户有错误发生。

2.1 nextTick

官方解释:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

$nextTick的作用是将回调延迟到下次DOM更新周期之后执行

下次DOM更新周期的意思是下次微任务执行时更新DOM,而vm.$nextTick其实是将回调添加到微任务中(只有在特殊情况下才降级到宏任务),而当我们自己调用nextTick的时候,它就在更新DOM的那个微任务后追加了我们自己的回调函数,从而确保我们的代码在DOM更新后执行

2.1.1 vue中dom的更新

Vue中DOM更新是异步的- --Vue 在数据变化之后,视图不会立刻更新,而是等同一事件循环中所有的数据变化完成之后再进行统一是数据更新

 Vue在更新dom时是异步执行的。只要监听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓存时去重对于避免不必要的计算和dom操作是非常重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用。 为什么 Vue 更新 DOM 要用异步操作?

在某些情况下,如果在页面加载的时候对对象 test 进行循环100次的赋值操作;如果不是异步更新,那么每一次循环赋值时都会根据响应式触发 setter => Def => Watcher => update => run 这样的操作,每一次都会更新视图,这样是非常消耗性能的。

2.2.2 什么时候使用nextTick

  • 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中

在created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted()钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。

  • 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。