iT邦幫忙

2024 iThome 鐵人賽

DAY 26
1
Modern Web

欸你是要進 Vue 了沒?系列 第 26

欸你是要進 Vue 了沒? - Day26:Vue 模板引用之使用 ref 直接操作 DOM 的小小法術

  • 分享至 

  • xImage
  •  

Hi 大家好,今天我們要來看的是「模板引用」的章節。
還記得之前我們提到的 ref 嗎?

ref 除了可以用來綁定響應式的狀態,它還能被使用在 <template> 模板中,幫助我們操作 DOM 元素(招式真多)

今天會學習到的部分:

  • 模板引用定義、語法
  • 訪問模板引用
  • 模板引用 vs. 偵聽器
  • v-for 中的模板引用
  • 函數模板引用

今天也會提到一點 生命週期偵聽器,一起 review 熱身一下🌟
然後我們就開始吧!

/images/emoticon/emoticon35.gif

定義

ref 可以作為一個特殊的屬性,讓我們指定我們想要操作的「DOM 元素」。

例如,如果我們想與頁面上的 input 元素建立關聯,可以在 input tag 上綁定 ref 屬性來實現。

注意:由於 DOM 元素操作是需要在掛載後才可以操作的,因此通常會搭配 onMounted() 函式來定義我們要操作的邏輯。

語法

const 自定義變數 = ref(渲染前,元素的預設值);

// 在模板中綁定一個 DOM 元素(ref 屬性的值 = 自定義變數,需要和 <script> 定義的同名)
<綁定的元素 ref="自定義變數" />

(宣告方式以需求為準,這邊以 const 示例)

官方文件:這可能很有用,例如說在組件掛載時將焦點設置到一個 input 元素上,或在一個元素上初始化一個第三方庫。

小範例

const input = ref(null);

// 在模板中綁定 input 元素(ref 屬性的值 = input,和 <script> 定義的同名)
<input ref="input" />

訪問模板引用

來看看掛載完成後,如何使用它吧!

實際情境可能會是:

我們想要在組件掛載後,對頁面上的 input 元素進行操作。

而在 script setup 定義的變數、函式,是在創建階段被創造實例的,而對於 DOM 的操作必須在「組件掛載完成後」進行,這時候就需要 cue 到 onMounted()!

因此我們可以這麼做:

  1. 於 script setup 定義 ref 響應式狀態,初始化 DOM 元素。
  2. 使用 onMounted() 定義掛載後要更新這個 DOM 元素的操作邏輯。
  3. 將在 script setup 中定義的響應式變數,和 template 中 DOM 元素上的 ref 屬性綁定。
<script setup>
import { onMounted, ref } from "vue";

const input = ref(null);

onMounted(() => {
  input.value.value = "我是預設文字";
});
</script>
<template>
  <input ref="input" />
</template>

以上我們做了什麼:

  1. <script setup> 中定義:const input = ref(null);:設置初始值為 null,並綁定一個變數 input
    input 變數就是在模板中要操作的 ref 屬性的值。
    (注意:由於渲染前拿不到值,所以初始值為 null 就可以~)
  2. 定義 onMounted() 函式,於掛載完成後,將 input 元素的值設置為 我是預設文字

https://ithelp.ithome.com.tw/upload/images/20241010/201691397mSEmLRink.png

若我們於 onBeforeMount() 中操作:
https://ithelp.ithome.com.tw/upload/images/20241010/20169139MKkQdO9tEV.png

就會報錯~(有懂生命週期的重要性了嗎!)

模板引用 vs. 偵聽器

偵聽模板引用時,需要注意:在組件掛載完成之前,模板中的元素尚未生成,因此該 DOM 元素的值會是 null

而我們可以使用「判斷式」來確認它是否存在。

watchEffect(() => {
  if (input.value) {
  // 掛載完成,元素存在時
    input.value.value = "我是預設文字";
  } else {
  // 掛載前、或卸載完成時
  }
})

v-for 中的模板引用

需要 v3.2.25 及以上版本

當在 v-for 中使用模板引用時,對應的 ref 中包含的值是一個數組,它將在元素被掛載後包含對應整個列表的所有元素

我們可為其宣告一個掛載前的值 [],綁定到 ref 屬性上,再搭配 v-for 的語法使用。

可以這麼做:

<script setup>
import { onMounted, ref } from "vue";

const list = ref(["a", "b", "c"]);
const itemRefs = ref([]);

onMounted(() => {
  console.log(list.value);
});
</script>
<template>
  <li v-for="item in list" ref="itemRefs">{{ item }}</li>
</template>

在掛載階段,v-for 將渲染出 list 中響應式狀態的陣列元素,而掛載後,印出 list.value
https://ithelp.ithome.com.tw/upload/images/20241010/20169139OZkVpOTdDQ.png

注意事項

官方文件:應該注意的是,ref 數組並不保證與源數組相同的順序。

由於 ref 屬性綁定的是 itemRefs,這個陣列是在完成掛載、渲染後生成的,因此它根據的是:DOM 元素的實際渲染順序,不一定會與 list 陣列元素的原始順序一致。

函數模板引用

模板引用除了一般的變數形式,也可以使用「函式」作為參數,而我們需要使用動態綁定 :ref

語法

<綁定的元素 :ref="自定義的函式" />

特性

  1. 自定義函式中傳入的「參數」會直接代表:綁定的 DOM 元素。
    所以可以在函式內直接對該元素進行操作或設定。
  2. 每次更新時函式都會被觸發。
  3. 當元素卸載時,也會再觸發一次(卸載後值就是 null)。

我們來看看實際例子:

<template>
  <input
    :ref="(el) => { if (el) { el.value = '我是預設文字'; } }"/>
</template>
  1. input 元素上使用動態綁定:
    :ref 綁定了 (el) => { if (el) { el.value = '我是預設文字'; } } 這個函式,而 el 代表渲染完成的 DOM 元素,因此在函式中,可以直接對 el 執行渲染完成後的邏輯。
  2. 在頁面掛載完成後,執行 el.value = '我是預設文字';

備註:每次更新都會被觸發,當元素卸載也會再觸發一次。

我們來加點邏輯,看看它卸載後的觸發!

// referenceChild.vue

<template>
  <input
    :ref="
      (el) => {
        if (el) {
          console.log('元素掛載:', el); // 元素掛載時
          el.value = '我是預設文字';
        } else {
          console.log('元素卸載:', el); // 元素卸載時,el 為 null
        }
      }"/>
</template>

然後我們使用一個父組件控制它的呈現:

<script setup>
import ReferenceChild from "./referenceChild.vue";
import { ref } from "vue";

const show = ref(true);
const change = function () {
  show.value = !show.value;
};
</script>
<template>
  <ReferenceChild v-if="show" />
  <button @click="change">切換卸載</button>
</template>

我們看看瀏覽器上的效果:

子元素被卸載後,確實也觸發了在 :rel 中定義的邏輯判斷 console.log('元素卸載:', el)

組件上的 ref

這一小節假設你已了解組件的相關知識,或者你也可以先跳過這裡,之後再回來看。

跳級生先看~我稍後補(噓)

小結

直接用屬性定義在掛載後要對 DOM 元素做的操作還蠻酷的(小腦袋有滿足到 🎼🎼🎼)而明天~我們要來補齊 Vue 一直在提到的 組件基礎 囉~~
/images/emoticon/emoticon11.gif/images/emoticon/emoticon11.gif

範例 code ⬇️

https://github.com/Jamixcs/2024iThome-jamixcs/tree/main/src/components/day26

參考資料


上一篇
欸你是要進 Vue 了沒? - Day25:Vue 組件偵聽器之 watch && watchEffect 是在襪取什麼東東
下一篇
欸你是要進 Vue 了沒? - Day27:Vue 組件基礎之 Component 的定義 && 使用 && 註冊
系列文
欸你是要進 Vue 了沒?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言