發布於 2023-03-18

更新於 2023-03-21

程式

約1266字 (7分鐘閱讀)

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

Title Image

Photo by Emily Morter on Unsplash

前言 - Prologue

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

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

正文 - Main text

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

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

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

可以使用 Vue 的 watch 屬性,通過觀察 DOM 元素是否已經存在,然後進行相應的操作。

示例:

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

<script>
export default {
  data() {
    return {
      myText: "Hello, World!"
    };
  },
  watch: {
    "$refs.myDiv": {
      handler(newVal) {
        // 在這裡進行相應的 DOM 操作
        console.log("myDiv 已經存在:", newVal);
      },
      immediate: true
    }
  },
  computed: {
    // 這裡可以直接使用 $refs.myDiv,因為 watch 已經確保了其存在
    myComputedText() {
      return this.$refs.myDiv.innerText.toUpperCase();
    }
  }
};
</script>

上方使用了 watch 屬性來觀察 DOM 元素的存在。在 "$refs.myDiv"handler 函數中進行了相應的 DOM 操作。同時在 watch 中使用了 immediate 選項,使得 handler 函數在初始化時就能夠執行。

immediate 是 Vue.js 中的一個 watch 選項,用於在監聽到資料變化後立刻執行 watch 的回調函數,而不是在下一次變化時執行。immediate 默認為 false,若設置為 true,則會在監聽時立刻執行回調函數。

computed屬性中可以直接使用 $refs.myDiv,是因為 watch 已經確保了其存在。

總之通過使用 Vue 的 watch 屬性可以避免在 computed 屬性中使用 $refs 時出現 undefined 的問題。

方法二:使用 $nextTick 延遲取得 $refs

通過 Vue 的 $nextTick 方法來解決在 computed 屬性中使用 $refs 時出現 undefined 的問題。

這個方法也是可行的,原理是在 DOM 渲染完成後再進行相應的 DOM 操作。

示例:

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

<script>
export default {
  data() {
    return {
      myText: "Hello, World!"
    };
  },
  computed: {
    myComputedText() {
      this.$nextTick(() => {
        // 在這裡進行相應的DOM操作
        console.log("myDiv 已經存在:", this.$refs.myDiv);
      });
      return this.$refs.myDiv.innerText.toUpperCase();
    }
  }
};
</script>

上方使用了 $nextTick 方法來確保 DOM 元素已經渲染完成。在 $nextTick 的回調函數中,可以進行相應的 DOM 操作。這樣在 computed 屬性中就可以直接使用 $refs.myDiv,因為 $nextTick 方法已經確保了其存在。

總之使用 $nextTick 方法也是解決在 computed 屬性中使用 $refs 時出現 undefined 的問題的方法,它的原理是通過等待 DOM 渲染完成後再進行相應的 DOM 操作。

方法三:computed 屬性加入 isMounted 變數

通過引入 isMounted 變數,來解決在 computed 屬性中使用 $refs 時出現 undefined 的問題。

isMountedtrue 時,computed 屬性中就可以正常使用 $refs

示例:

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

<script>
export default {
  data() {
    return {
      isMounted: false,
      myText: "Hello, World!"
    };
  },
  computed: {
    myComputedText() {
      if (!this.isMounted) return;
      return this.$refs.myDiv.innerText.toUpperCase();
    }
  },
  mounted() {
    this.isMounted = true;
  }
};
</script>

上面引入了一個 isMounted 變數,並在 computed 屬性中判斷其值,來解決在使用 $refs 時出現 undefined 的問題。在 mounted 生命週期中將 isMounted 設置為 true,從而去允許 computed 屬性中使用 $refs

而需要注意的是,computed 屬性中返回一個常數時不會觸發資料的雙向綁定。因此若需要使用 computed 屬性來進行資料的雙向綁定,則需要返回一個可變的對象或陣列。

總之這種方法也是可行的,原理是通過引入一個 isMounted 變數來解決 computed 屬性中使用 $refs 時出現 undefined 的問題。


Other

除了上述的三種方法,應該還有一些可能的其他解决方法(?),像是下列兩種:

  1. 使用 $el 代替 $refs 屬性
  2. mounted 中使用 setTimeout 延遲取得 $refs

參考資料 - Reference

[1] 解决 Vue computed 计算属性中使用 $refs 进行 dom 操作时出现 undefined 问题 _computed 使用 refs _ AwesomeDevin 的博客 - CSDN博客

https://blog.csdn.net/daivon_up/article/details/80149416

後記 - Epilogue

參考資料是當初的解決方法QQ,感謝各位分享知識的好心人ヽ(*・ω・)ノ!

明天有機會的話會繼續延續這個主體想一下還有沒有其他方法或是在 Vue3 該如何解決。

建置部落格時踩到的坑之一,紀錄一下 (o´∀`o)。

噹噹噹!續篇在這裡:

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

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

https:///blog/45

作者

Kama

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

Copyrights © 2023 Kama, All Rights Reserved.