溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Vue異步更新DOM及$nextTick執(zhí)行機(jī)制源碼分析

發(fā)布時間:2023-03-25 14:43:56 來源:億速云 閱讀:85 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“Vue異步更新DOM及$nextTick執(zhí)行機(jī)制源碼分析”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

Vue異步更新DOM策略

我們知道,Vue實現(xiàn)響應(yīng)式并不是數(shù)據(jù)發(fā)生變化之后DOM立即變化,而是按一定的策略進(jìn)行DOM的更新。

Vue 在更新 DOM 時是異步執(zhí)行的。只要偵聽到數(shù)據(jù)變化,Vue 將開啟一個隊列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。如果同一個 watcher 被多次觸發(fā),只會被推入到隊列中一次。這種在緩沖時去除重復(fù)數(shù)據(jù)對于避免不必要的計算和 DOM 操作是非常重要的。然后,在下一個的事件循環(huán) “tick" 中,Vue 刷新隊列并執(zhí)行實際(已去重的)工作。

$nextTick執(zhí)行機(jī)制

$nextTick會在DOM更新之后被觸發(fā),以獲取最新DOM節(jié)點(diǎn)。

具體來講:我們使用 JavaScript 進(jìn)行原生DOM操作時,隨著 JavaScript 代碼執(zhí)行會同步進(jìn)行DOM更新;而使用 Vue 則會異步更新 DOM,會在當(dāng)前執(zhí)行棧的最后更新DOM。

對于兼容的瀏覽器來說,nextTick相當(dāng)于是微任務(wù),即$nextTick的回調(diào)函數(shù)是在當(dāng)前執(zhí)行棧的所有同步任務(wù)執(zhí)行完畢后再執(zhí)行,所以nextTick中會得到 DOM 更新后的結(jié)果。(不過頁面渲染始終是在微任務(wù)執(zhí)行之后才進(jìn)行的,所以$nextTick回調(diào)函數(shù)執(zhí)行時頁面還沒有進(jìn)行渲染,回調(diào)函數(shù)執(zhí)行時在頁面上看不到更新后的結(jié)果)

注:瀏覽器如果不兼容則有幾種備選方案,其中setTimeout是最后的一種備選方案,它會將回調(diào)函數(shù)加入任務(wù)隊列 task 中,等待執(zhí)行。

示例詳解

JavaScript原生DOM操作(隨著JS代碼的執(zhí)行同步進(jìn)行DOM更新):

<body>
    <ul id="ul1">
        <li>100</li>
        <li>200</li>
        <li>300</li>
    </ul>

    <script>
        const ul1 = document.getElementById('ul1')
        console.log(ul1.children.length); // 3
        const newLi = document.createElement('li')
        newLi.innerHTML = '400'
        ul1.appendChild(newLi)
        console.log(ul1.children.length); // 4
        Promise.resolve().then(() => {
            alert(ul1.children.length) // 彈出4,此時頁面還沒有進(jìn)行渲染(頁面還未展示內(nèi)容)
        })
    </script>
</body>

Vue異步更新DOM:

<template>
  <div id="app">
    <ul ref="ul1">
      <li v-for="(item, index) in list" :key="index">
        {{ item }}
      </li>
    </ul>
    <button @click="addItem">添加一項</button>
  </div>
</template>

<script>
export default {
  name: "app",
  data() {
    return {
      list: ["a", "b", "c"],
    };
  },
  methods: {
    addItem() {
      this.list.push(`${Date.now()}`);
      this.list.push(`${Date.now()}`);
      this.list.push(`${Date.now()}`);

      // 獲取 DOM 元素
      const ulElem = this.$refs.ul1;
      // eslint-disable-next-line
      console.log(ulElem.childNodes.length);// 3

      // 1. 異步進(jìn)行 dom 更新,$nextTick 待 DOM 更新完再回調(diào)
      // 2. dom 更新時會將 data 的修改做整合,多次 data 修改只會更新一次
      this.$nextTick(() => {
        // eslint-disable-next-line
        console.log(ulElem.childNodes.length); // 6

        alert(ulElem.childNodes.length); // 彈出 6,此時頁面還沒有進(jìn)行渲染(頁面還沒展示新添加的3個元素)
      });
    },
  },
};
</script>

Vue異步更新DOM的目的

因為如果同步進(jìn)行DOM更新,則每次對響應(yīng)式數(shù)據(jù)進(jìn)行修改就都會觸發(fā)setter -> 通知watcher -> 觸發(fā)re-render -> 生成new vnode(vdom) -> patch(更新真實DOM)。

如果每次修改數(shù)據(jù)都會走一遍這個流程是非常消耗性能的,所以使用異步更新 DOM 的策略,先對數(shù)據(jù)修改進(jìn)行整合,再使用最終的整合結(jié)果一次性對 DOM 進(jìn)行更新。

$nextTick應(yīng)用示例

<template>
  <div>
    <button @click="callbackFun">點(diǎn)我展示輸入框并自動獲取焦點(diǎn)</button> <br />
    <input type="text" v-show="test" ref="input" />
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      test: false,
    };
  },
  methods: {
    callbackFun() {
      this.test = true;
      this.$nextTick(function () {
        this.$refs.input.focus(); // 若該行代碼不放在 $nextTick 中,則無法展示輸入框后自動獲取焦點(diǎn)
      });
    },
  },
};
</script>

“Vue異步更新DOM及$nextTick執(zhí)行機(jī)制源碼分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI