iT邦幫忙

2024 iThome 鐵人賽

DAY 15
1
Modern Web

Vue 3 初學者:用實作帶你看過核心概念系列 第 15

Vue 3 用實作帶你看過核心概念 - Day 15:監聽器(Watchers)

  • 分享至 

  • xImage
  •  

文章背景圖

目錄

  • watch 基本使用方式
  • watch 深層監聽器 - deep option
  • watch 立即執行的監聽器 - immediate option
  • watch 觸發時間 - flush option
  • 動態創建 watch 監聽器
  • 總結

watch 基本使用方式

在響應式狀態變化時執行特定操作,需要使用watch 監聽器。這些操作可能包含副作用 (side effects),如發送請求、調用 API,或更新 DOM 以外的系統狀態,亦或是處理自定義的邏輯。

watch可以監聽單一或多個響應式數據的變化。與 computed 計算屬性不同,watch 的側重點不在於計算派生值,而在於觀察響應狀態變化後觸發的動作。

watch 可以綁定在數據計算屬性getter 函式上,偵測值變更前變更後的變化並做出響應。

這邊針對字串純值輸入框、物件單一屬性輸入框及陣列索引值輸入框變更並觸發對應的監聽器為例子說明使用方式:
👉 Vue3 Options API Watch 基本使用方式實作連結

watch 字串、物件、計算屬性範例展示圖

字串純值輸入框 Vue Template:

<input v-model="inputPureVal" id="inputVal" type="text">

javascript:

inputPureVal(newValue, oldValue) {
  this.inputPureValMsg = `oldValue:${oldValue},newValue:${newValue}`;
}

物件單一屬性輸入框 Vue Template:

<input v-model="inputObjVal.name" id="inputObjVal" type="text">

javascript:

"inputObjVal.name": function (newValue, oldValue) {
  this.inputObjValMsg = `oldValue:${oldValue},newValue:${newValue}`;
}

⭐ watch 支持使用.的語法來監聽簡單的屬性路徑,但不支持使用像inputObjVal[name]這樣的動態屬性名稱來監聽。


陣列索引值輸入框 Vue Template 搭配計算屬性:

<input v-model="inputArrVal[0]" id="inputArrVal" type="text">
<input v-model="inputArrVal[1]" id="inputArrVal" type="text">

計算屬性:
computed javascript:

computed: {
    getCompositionItem() {
      return `第一項的值:${this.inputArrVal[0]},第二項的值:${this.inputArrVal[1]}`;
    }
}

watch javascript:

getFirstItem: function (newValue, oldValue) {
  this.inputArrMsg = `oldValue:${oldValue},newValue:${newValue}`;
}

watch 深層監聽器 - deep option

前面的範例可以知道,watch監聽器可以監聽物件內特定屬性的變化,或通過計算屬性來監控特定陣列索引值的變動。這是因為watch預設是淺層監控,也就是說,它只監控被觀察對象的第一層變化,因此直接監控整個物件無法得知其內部嵌套屬性的變動。

在這種情況下,可以使用watchoption設置來解決問題。首先,當需要深度監控物件時,可以使用deep選項。若要使用這些選項,必須以物件形式來定義監聽器。這個物件應包括監控對象變動的 handler函數,以及相關的option設置。

透過巢狀的物件結構搭配watchdeep option案例觀看其變化:
👉 Vue3 Options API watch 搭配 deep option 實作連結

watch 監聽響應式變數(物件)

⭐ 當監控的對象不是純值而是一個物件時,因為物件的引用來源相同,即使觸發變更並修改了物件內的屬性,變化前後的值 (newValue 和 oldValue) 仍然指向同一個物件。因此,它們會相等。

Vue Template:

<input v-model="inputObjVal.likefruit[0]" id="inputObjVal" type="text">

Vue 數據巢狀結構:

data() {
    return {
      inputObjVal: {
        name: "Antonio",
        age: 18,
        // 深層物件
        likefruit: ["Apple", "Banana", "Orange"]
      },
    };
}

watch 立即執行的監聽器 - immediate option

watch預設是懶執行的,也就是當監控對象的狀態發生變化時才會觸發執行,而不會在初始化時立刻執行。這與computed計算屬性不同,computed在初始化時就會立即執行計算。如果需要watch在監聽器初始化時立即執行一次,可以搭配immediate option來實現。

透過查詢會員資料搭配 fakeapi 在初始化結束立即執行一次查詢的案例說明:
👉 Vue3 Options API watch 搭配 immediate option 實作連結

Vue Template:

<div id="app">
  <label for="userid">用戶編號查詢(輸入完按 Enter):</label>
  <input v-model.lazy="userid" type="text" id="userid" class="mb-4">
  <div class="content">
    <h2>API 返回資料:</h2>
    <div v-show="!isLoading" class="text">{{ userInfo }}</div>
    <div v-show="isLoading" class="text">資料讀取中。。。</div>
  </div>
</div>

javascript:

watch: {
    userid: {
      async handler(newValue, oldValue) {
        this.showLoading()
        const data = await this.getUserinfo();
        this.userInfo = data;
        this.isLoading = false;
      },
      immediate: true
    }
}

watch 觸發時間 - flush option

Vue 的響應式系統依賴於事件循環。當數據發生變化時,Vue 不會立即同步地更新 DOM,而是會將所有變更操作合併到同一個事件循環中,等這個循環結束後才批次更新 DOM。這可以提高性能,避免多次重複更新導致不必要的開銷。

watch 監聽器的回調時機可以通過flush option來調整監聽器的觸發時機:

  • flush: 'pre'
    • 預設情形。回調函式會在數據發生變化後觸發,但這個觸發時間是在 DOM 更新之前。
    • 適合數據變化的時候即使處理邏輯。
  • flush: 'post'
    • 回調在數據變化並且 DOM 更新完成後的下一個事件循環中執行,確保 DOM 元素已更新完成。
    • 適合需要操作更新後的 DOM 元素的情形。
  • flush: 'sync'
    • 同步觸發回調,即在數據變化的同一事件循環中立即執行回調。
    • 適合需要立即執行的監聽操作,但可能會影響性能,因為這會使得監聽器同步執行而非批次處理。

比較flush:'pre'flush:'post'兩者呈現的差別:
👉 Vue3 Options API watch option flush:"pre" vs flush:"post" 差異實作連結

流程說明:

  1. 紅色區塊綁定stye height的樣式
  2. 每次點擊按鈕紅色區塊動態的style height增加
  3. 綁定watch監聽器,當style height變化的時候,在回調函式內印出watch 變化前後的差別,並且直接讀取content DOM 元素高度進行比較。

watch flush option pre 與 post 比較

Vue Template:

<div :style="contentStyle" ref="content" class="content"></div>
<p>{{ usePostWatchMsg }}</p>

javascript:

  watch: {
    "contentStyle.height": {
      handler(newValue, oldValue) {
        this.usePostWatchMsg = `watch 舊高度:${oldValue} | watch新高度:${newValue} | 讀取 DOM 元素高度:${this.$refs.content.clientHeight}`;
      },
      flush: "post"
    }
  }

比較flush:'pre'flush:'sync'兩者呈現的差別:
👉 Vue3 Options API watch option flush:"pre" vs flush:"sync" 差異實作連結

流程說明:

  1. 方法定義increment並在數據改變前後印出 before 及 after 的訊息
  2. 監聽器監聽數據改變的時候,在 watch 回調函數印出數據改變新的值

watch flush option pre 與 sync 比較

javascript:

const rootComponent = {
  data() {
    return {
      count: 0,
      orderMsg: []
    };
  },
  methods: {
    increment() {
      this.orderMsg.push("methods increament before");
      this.count += 1;
      this.orderMsg.push("methods increament after");
    }
  },
  watch: {
    count: {
      handler(newValue) {
        this.orderMsg.push(`watch option pre newValue:${newValue}`);
      },
      flush: "sync"
    }
  }
}

動態創建 watch 監聽器

在某些特定場景下,如果需要根據條件動態建立監聽器,可以使用 Vue 的this.$watch()方法手動設定監聽器。此外,根據邏輯需求,可以主動銷毀這些動態建立的監聽器。
👉 Vue3 Options API 動態創建 watch 監聽器實作連結

流程說明

  1. 當點擊【AI 辨識啟動】的時候,主動創建監聽器並儲存在unwatch數據當中
  2. 在此階段輸入內容會觸發動態監聽器的回調函式,顯示辨識出的幸運數字
  3. 點選【AI 辨識關閉】的時候,透過this.unwatch()銷毀創建的動態監聽器

javascript:

startWatching() {
  this.isWatching = true;
  this.unwatch = this.$watch(
    "inputVal",
    (newValue, oldValue) => {
      this.getAiMsg();
    },
    { immediate: true }
  );
},
// 銷毀動態創建的監聽器
stopWatching() {
  if (this.unwatch) {
    this.unwatch();
    this.unwatch = null;
  }
  this.isWatching = false;
  this.aiMsg = ""; // 重置 AI 消息
}

總結

  • watch 監聽並執行對應邏輯處理:相較於computed根據響應式數據變化派生計算結果,watch更側重於在數據變化時執行特定邏輯,如操作 DOM 元素、發送 API 請求等副作用。它可以觀察到數據變化前後的值(oldValue 和 newValue),並根據變化執行相應的操作。
  • watch 監控對象的多樣式:可以監控響應式變數、計算屬性等多種數據來源。若需監控物件內部屬性,可使用.的方式串接路徑,精確的監聽單一屬性,避免不必要的效能消耗。
  • watch 監控選項(option):當需要使用watch的監控選項(option)時,需要以對象的方式定義監聽器,並使用handler作為回調函式來處理,才可以啟用相關選項:
    • deep:深度追蹤物件所有屬性變化,在資料結構複雜的情況下可能會導致效能問題。
    • immediate:watch預設為懶執行,只有在依賴的響應式數據發生變化時才會觸發。若需要在初始化時立即執行,可以使用immediate選項。
    • flush:用於控制watch回調的觸發時機,包括pre(預設,數據變化後但 DOM 更新之前)、post(DOM 更新完成後)、和sync(同步立即執行)。
  • watch 動態創建:在特定情境下,可以根據需求動態創建 watch 監聽器,並根據業務邏輯手動銷毀。

上一篇
Vue 3 用實作帶你看過核心概念 - Day 14:生命週期鉤子(Lifecycle Hooks)
下一篇
Vue 3 用實作帶你看過核心概念 - Day 16:使用 ref 屬性引用模板元素
系列文
Vue 3 初學者:用實作帶你看過核心概念30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言