焦点信息:带你深入了解下this.$nextTick!

时间:2022-12-21 21:06:37       来源:转载


(资料图片仅供参考)

都快2023年,你还不会this.$nextTick吗,Vue2都出快10年了,Vue3也已经出了两年多了,没错!说出来就丢脸,我现在才会this.nextTick(实话实说),好的咱先百度一下,哒哒哒....网速飞快地跳到了Vue.js官网文档,突然发现文档中有一言?:

我们先看看nextTick究竟是个啥?

console.log(this.$nextTick);// 控制台打印if(fn){  return nextTick(fn, this);}
登录后复制

我们可以看出nextTick就是一个方法,方法有两个参数:fn和this,fn就是需要传的回调函数,this就是所说的执行环境上下文。那么问题来了,在Vue中是如何实现在下一次DOM更新结束之后才会执行延迟回调的?我们先看看下面的例子:

created:{{message}}
// vue实例data: { message: "Hello World!",},created(){ this.message = "你好,世界!"; console.log(this.$refs.test1.innerText);// 报错 // TypeError: Cannot read properties of undefined (reading "innerText") this.$nextTick(()=>{ console.log("test1 nextTick:",this.$refs.test1.innerText);// 你好,世界! });},
登录后复制

从上面例子中,在created生命周期中操作了DOM,但是我们都知道created生命周期只是初始化了数据,这期间是还没有渲染DOM的,如果我们直接操作DOM是找不到DOM元素的,那么问题来了:为什么放在nextTick中就可以获取到了DOM元素呢?这不是很明显吗,等到DOM渲染完才调用不就获取到了吗,从而知道了nextTick作用就是用来等下次DOM渲染完才去调用nextTick内的DOM操作代码。那么问题又来了,nextTick究竟做了什么?下面我们一起从源码层面来分别分析Vue2和Vue3版本的nextTick原理是啥。

Vue2版本的nextTick

由于Vue暴露给开发者的是nextTick这个方法,在这个方法中主要做了三件事,回调函数的添加延迟执行回调函数判断当前的nextTick是否传入回调函数。不传的话,是一个Promise,this.$nextTick.then(()=>{}),按Promise处理。

回调函数添加入callbacks数组,因为可能有多个nextTick函数在当前作用域中。判断当前nextTick是否已经标记为pending=true,也就是正在执行,如果不是就执行timerFunc(异步执行函数 用于异步延迟调用 flushCallbacks 函数)。timerFunc的执行,判断当前环境是否支持promise、MutationObserver、setImmediate、setTimeout,优先级高低从前到后,分四种情况:优先使用Promise,如果当前环境支持promise,nextTick默认优先使用promise去执行延迟回调函数,timerFunc执行的是Promise,promise是es6下的语法,如果当前环境只支持es6以语法下的,只能考虑后面支持情况了。支持MutationObserver,HTML5的api,中文意思是:修改的监听,MutationObserver用来监听DOM的变动,比如节点的增减、属性的变动、文本内容的修改等都会触发MutationObserver事件。注意地,与事件不同,事件是同步触发,DOM的变动会立即触发事件,而MutationObserver事件是异步触发,DOM不会立即触发,需要等当前所有DOM操作完毕才会触发。

为什么需要创建一个文本节点?因为在这里操作DOM保证浏览器页面是最新DOM渲染的,虽然看来好像是没什么作用,但这是保证拿到的DOM是最新的。

支持setImmediatesetTimeout,setImmediate即时计时器立即执行工作,它是在事件轮询之后执行,为了防止轮询阻塞,每次只会调用一个。setTimeout按照一定时间后执行回调函数。

好了好了,到了现在,我们都知道nextTick做了什么吧,但是我们有没有想过这样的一个问题:既然都是异步回调执行等待DOM更新后才去调用操作DOM的代码,那么这个机制又是什么原理?这就是JS的执行机制有关了,涉及宏任务与微任务的知识,我们先来看看这样的一道题:

console.log("同步代码1");setTimeout(function () {    console.log("setTimeout");}, 0);new Promise((resolve) => {    console.log("同步代码2")    resolve()}).then(() => {    console.log("promise.then")})console.log("同步代码3");
登录后复制

我们可能会问上面的输出是个啥,首先js是单线程,所以在js程序运行中只有一个执行栈,实现不了多线程,所以就需要任务均衡分配,通俗的讲,按任务急优先处理原则,js中分为同步任务和异步任务,异步任务又分为宏任务和微任务,同步任务先入栈,程序会先把执行栈中的所有同步任务执行完,再去判断是否有异步任务,而异步任务中微任务的优先级高于宏任务。如果当前执行栈为空,而微任务队列不为空,就先执行微任务,等把所有微任务执行完,最后才会考虑宏任务。而上面代码中Promise是属于微任务,而setTimeout是宏任务,所以上面的输出为:

// 同步代码1// 同步代码2// 同步代码3// promise.then // setTimeout
登录后复制

使用Vue2的nextTick

传入回调函数参数使用:

this.$nextTick(()=>{  // ...操作DOM的代码})
登录后复制

不传入回调函数参数使用:

// 方式一this.$nextTick().then(()=>{  // ...操作DOM的代码})// 方式二await this.$nextTick();// 后写操作DOM的代码
登录后复制

Vue3版本的nextTick

Vue3版本就没有Vue2版本的那么多环境支持,nextTick封装成了一个Promise异步回调函数执行。

// Vue3.2.45// core-main\core-main\packages\runtime-core\srcexport function nextTick(  this: T,  fn?: (this: T) => void): Promise {  const p = currentFlushPromise || resolvedPromise  return fn ? p.then(this ? fn.bind(this) : fn) : p}
登录后复制

使用Vue3的nextTick

传入回调函数使用

import { nextTick } from "vue" // 引入setup () {    nextTick(()=>{   // ...操作DOM的代码})
登录后复制

不传入回调函数的使用

import { nextTick } from "vue" // 引入setup () {        // 方式一    nextTick().then(()=>{      // ...操作DOM的代码    })    // 方式二    await nextTick();    // 后写操作DOM的代码}
登录后复制

总结

nextTick可以通俗的当作一个Promise,所以nextTick属于微任务。nextTick在页面更新数据后,DOM更新,可以通俗理解为,nextTick就是用来支持操作DOM的代码及时更新渲染页面。也就是在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。

(学习视频分享:vuejs入门教程、编程基础视频)

以上就是带你深入了解下this.$nextTick!的详细内容,更多请关注php中文网其它相关文章!

关键词: 回调函数 生命周期 深入了解