Released 2023-03-20

Modified 2023-03-21

Tech

1431Words (8min read)

[Day 27] Vue computed 如何處理 $refs 中的 undefined 問題 Part2 - 嘗試 30 日寫文充版挑戰

Title Image

Photo by Emily Morter on Unsplash

前言 - Prologue

嘗試寫 30 天文充實版面 (跳過假日 ლ(́◕◞౪◟◕‵ლ) ) 更新文章的 Day 27

接續上一篇貼文 [Day 26] Vue computed 如何處理 $refs 中的 undefined 問題 Part1 - 嘗試 30 日寫文充版挑戰 所述的其他可能的解決方法。

上一篇前言文案:

在 Vue 中使用 computed 屬性和 $refs 來進行 DOM 操作是一個常見的需求,但是有時會遇到 computed 屬性中使用 $refs 時出現undefined 的問題。本文將介紹多種解決這個問題的方法使我們能夠順利地在 Vue 中使用 computed$refs 進行 DOM 操作。

正文 - Main text

當在 Vue 的 computed 計算屬性中使用 $refs 進行 DOM 操作時,會出現 undefined 的問題,這是因為computed 屬性首次執行時, DOM 元素還尚未被渲染出來,所以 $refs 還不存在。

目前想到有五種方法,本篇將介紹其中二篇,其餘可在 Part1 找到。

  1. watch 中監聽 $refs 是否變化
  2. 使用 $nextTick 延遲取得 $refs
  3. computed 屬性加入 isMounted 變數
  4. 使用 $el 代替 $refs 屬性
  5. mounted 中使用 setTimeout 延遲取得 $refs
方法四:使用 $el 代替 $refs 屬性

$el 屬性是 Vue 實例所掛載的元素,可以通過 this.$el 來取得。因為在 computed 屬性計算時,元素已經被掛載到 Vue 實例上了,所以使用 $el 可以避免未定義的問題。

computed: {
  computedProp() {
    // 使用 $el 替代 $refs
    const el = this.$el.querySelector('.my-element')
    return el ? el.offsetHeight : 0
  }
}

示例1 - 使用計算屬性:

<template>
  <div>
    <div class="my-element">{{ myText }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      myText: "Hello, World!"
    };
  },
  computed: {
    myComputedText() {
      const el = this.$el.querySelector(".my-element");
      if (el) {
        return this.myElement.innerText.toUpperCase();
      } else {
        return "";
      }
    }
  }
};
</script>

上面例子在 myComputedText 計算屬性中使用了 this.$el 訪問了 Vue 實例的根 DOM 元素,然後使用 querySelector 方法選擇了 .my-element 元素。如果 .my-element 元素存在,我們就可以在 myComputedText 計算屬性中進行相應的 DOM 操作,例如:示例中將文字轉換為大寫。如果 .my-element 元素不存在,則返回一個空字符串。

注意:由於在模板中定義的 DOM 元素還沒有被完全掛載到 Vue 實例上,因此必須在 computed 中訪問 $el 屬性。

computed 中進行 DOM 操作有一定的風險,因為 computed 是基於它所依賴的數據進行緩存的,如果沒有正確地管理依賴關係,可能會導致計算結果的不準確或無法更新。

建議使用 $refs 屬性來操作 DOM 元素,這樣可以更加安全和方便地訪問 DOM 元素。


當使用 $el 屬性代替 $refs 屬性時,需要確保對應的 DOM 元素已經被掛載到 Vue 實例上。

示例2 - 使用方法屬性:

<template>
  <div>
    <button @click="handleClick">Click me</button>
    <div class="my-element">{{ myText }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      myText: "Hello, World!"
    };
  },
  methods: {
    handleClick() {
      const el = this.$el.querySelector(".my-element");
      if (el) {
        // 在這裡進行相應的 DOM 操作
        el.style.color = "red";
      }
    }
  }
};
</script>

上面例子通過 this.$el 訪問了 Vue 實例的根 DOM 元素,然後使用 querySelector 方法選擇了 .my-element 元素。如果 .my-element 元素存在,就可以在 handleClick 方法中進行相應的 DOM 操作,例如:將其文字顏色設置為紅色。

注意:由於在模板中定義的 DOM 元素還沒有被完全掛載到 Vue 實例上,因此必須在 handleClick 方法中訪問 $el 屬性。如果在 mounted 鉤子中訪問 $el 屬性,有可能無法訪問到 .my-element 元素,因為它還沒有被完全掛載到 Vue 實例上。

方法五:在 mounted 中使用 setTimeout 延遲取得 $refs

mounted 是 Vue 實例掛載完成的生命週期鉤子,可以在這個階段取得 $refs屬性,但為了確保取得 $refs,需要使用 setTimeout 進行延遲。

mounted() {
  setTimeout(() => {
    const el = this.$refs.myElement
    if (el) {
      // 獲取到 $refs 後進行相應的操作
    }
  }, 0)
}

示例:

<template>
  <div>
    <div ref="myElement">{{ myText }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      myText: "Hello, World!"
    };
  },
  mounted() {
    // 在 mounted 階段,$refs.myElement 還不存在
    console.log("$refs.myElement", this.$refs.myElement);
    // undefined

    // 使用 setTimeout 延遲一個微小的時間後,$refs.myElement 就可以正確地訪問
    setTimeout(() => {
      console.log("$refs.myElement", this.$refs.myElement);
      // <div>Hello, World!</div>
    }, 1);
  },
  computed: {
    myComputedText() {
      return this.$refs.myElement ? this.$refs.myElement.innerText.toUpperCase() : "";
    }
  }
};
</script>

上方例子在 mounted 鉤子使用 setTimeout 延遲了一個極短的時間,以確保 $refs.myElement 已經存在於 Vue 實例中。在 computed 屬性 myComputedText 中,如果 $refs.myElement 存在,則可以進行相應的 DOM 操作。

後記 - Epilogue

每種解決方法都有其適用的情況和限制。

例如:

  1. 使用 $nextTick 可以確保 $refs 屬性所對應的 DOM 元素已經被渲染出來,但需要額外的程式碼來處理異步操作。
  2. mounted 鉤子函數中訪問 $refs 屬性可以簡單地解決問題,但需要等待整個 Vue 實例都被渲染出來之後才能訪問 $refs 屬性。
  3. 使用 watch 屬性可以監聽 $refs 的變化,但可能會造成不必要的計算開銷和性能損失。
  4. 使用 $el 屬性代替 $refs 屬性可以避免未定義的問題,但在 computed 中進行 DOM 操作有一定的風險。

因此在使用這些解決方法時,需要根據具體情況斟酌使用哪一種解決方法,以達到最佳的效果,同時可以根據具體情況探索出更加適合的解決方法,以上都只是想到參考的解決方法,一定會有更佳的方法還沒探索出來。

[Day 27] Vue computed 如何處理 $refs 中的 undefined 問題 Part2 - 嘗試 30 日寫文充版挑戰

https:///en/blog/46

Author

Kama

Read more from Tech
lightbox-image
Toggle Button - Red Pandas Icons by svgrepo.com

Copyrights © 2023 Kama, All Rights Reserved.