首頁  >  文章  >  web前端  >  帶你深入了解下this.$nextTick!

帶你深入了解下this.$nextTick!

青灯夜游
青灯夜游轉載
2022-12-21 20:43:101975瀏覽

帶你深入了解下this.$nextTick!

都快2023年,你還不會this.$nextTick嗎,Vue2都出快10年了,Vue3也已經出了兩年多了,沒錯!說出來就丟臉,我現在才會this.nextTick(實話實說),好的咱先百度一下,噠噠噠....網速飛快地跳到了Vue.js官網文檔,突然發現文檔中有一言? :

nextTick:在下一次DOM更新迴圈結束後執行延遲回呼。在修改資料之後立即使用這個方法,取得更新後的DOM。它有兩個參數:第一個參數是回呼函數,不傳時提供promise呼叫;第二個參數是執行環境上下文,不傳預設是自動綁定到呼叫它的實例上。 【相關推薦:vuejs影片教學web前端開發

#我們先看看nextTick究竟是個啥?

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

我們可以看出nextTick就是一個方法,方法有兩個參數:fn和this,fn就是需要傳的回調函數,this就是所說的執行環境上下文。那麼問題來了,在Vue中是如何實現在下一次DOM更新結束之後才會執行延遲回調的? 我們先看看下面的例子:

<div ref="test1">created:{{message}}</div>
// vue实例
data: {
    message: "Hello World!",
},
created(){
    this.message = &#39;你好,世界!&#39;;
    console.log(this.$refs.test1.innerText);// 报错
    // TypeError: Cannot read properties of undefined (reading &#39;innerText&#39;)
    this.$nextTick(()=>{
         console.log(&#39;test1 nextTick:&#39;,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處理。

帶你深入了解下this.$nextTick!

  • 回呼函數會加入callbacks數組,因為可能有多個nextTick函數在目前作用域中。

帶你深入了解下this.$nextTick!

  • 判斷目前nextTick是否已經標記為pending=true,也就是正在執行,如果不是就執行timerFunc(非同步執行函數用於非同步延遲呼叫flushCallbacks 函數)。 timerFunc的執行,判斷目前環境是否支援promise、MutationObserver、setImmediate、setTimeout,優先權高低從前到後,分四種情況:
  • #優先使用Promise,如果目前環境支援promise,nextTick預設優先使用promise去執行延遲回呼函數,timerFunc執行的是Promise,promise是es6下的語法,如果當前環境只支援es6以語法下的,只能考慮後面支援情況了。

帶你深入了解下this.$nextTick!

#
  • 支持MutationObserver,HTML5的api,中文意思是:修改的监听,MutationObserver用来监听DOM的变动,比如节点的增减、属性的变动、文本内容的修改等都会触发MutationObserver事件。注意地,与事件不同,事件是同步触发,DOM的变动会立即触发事件,而MutationObserver事件是异步触发,DOM不会立即触发,需要等当前所有DOM操作完毕才会触发。

MutationObserver有7个属性:childList(true,监听子节点的变动)、attributes(true,监听属性的变动)、characterData(true,监听节点内容或节点文本的变动)、subtree(是否应用于该节点的所有后代节点)、attributeOldValue(观察attributes变动时,是否需要记录变动前的属性值)、characterDataOldValue(观察characterData变动时,是否需要记录变动前的值)、attributeFilter(数组,表示需要观察的特定属性(比如[‘class’,‘src’])。

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

帶你深入了解下this.$nextTick!

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

帶你深入了解下this.$nextTick!

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

console.log(&#39;同步代码1&#39;);
setTimeout(function () {
    console.log("setTimeout");
}, 0);
new Promise((resolve) => {
    console.log(&#39;同步代码2&#39;)
    resolve()
}).then(() => {
    console.log(&#39;promise.then&#39;)
})
console.log(&#39;同步代码3&#39;);

我们可能会问上面的输出是个啥,首先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\src
export function nextTick<T = void>(
  this: T,
  fn?: (this: T) => void
): Promise<void> {
  const p = currentFlushPromise || resolvedPromise
  return fn ? p.then(this ? fn.bind(this) : fn) : p
}

使用Vue3的nextTick

  • 传入回调函数使用

    import { nextTick } from &#39;vue&#39; // 引入
    setup () {    
    nextTick(()=>{
       // ...操作DOM的代码
    })
  • 不传入回调函数的使用

    import { nextTick } from &#39;vue&#39; // 引入
    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中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除