Skip to main content

Command Palette

Search for a command to run...

Watch 的情境使用

Updated
•3 min read•View as Markdown
Watch 的情境使用

watch 總共有三個參數

  1. val : 第一個為 被監聽的變數
  2. newVal : 當監聽數值變化時,產生的新數值
  3. oldVal : 當監聽數值變化時,產生的舊數值
watch(val, (newVal, oldVal) => {
    console.log(`new: ${newVal} ; old: ${oldVal}`);
});

ref & reactive watch 監控差異

下方範例測試簡單的 ref watch 範例來執行:

const { ref, reactive, watch } = Vue;
const App = {
  setup() {
    const num = ref(0);

    watch(num, (n, o) => {
      console.log(`new: ${n} ; old: ${o}`);
    });
    setInterval(() => {
      num.value++;
    }, 2000);

    return {
      num,
    };
  },
};

Vue.createApp(App).mount("#app");

而當今天如果要改為 reactive 的話,我們先用上方的邏輯來嘗試撰寫一次

const { ref, reactive, watch } = Vue;
const App = {
  setup() {
    const num1 = reactive({ idx: 0 });

    watch(num1, (n, o) => {
      console.log(`new: ${n} ; old: ${o}`);
    });
    setInterval(() => {
      num1.idx++;
    }, 2000);

    return {
      num1,
    };
  },
};

Vue.createApp(App).mount("#app");

結果會發現沒有任何 console 產生,並且在 devtool 新增了一個 warning 警告提示

![warn 警告](https://i.imgur.com/HaL44jY.png "warn 警告")

:::tip warn 警告提示意思是指 我現在 watch 能監控的數值,它只能被 getter,也就是只能被讀取的數值,所以需要透過一個函示,去將 reactive 的 key 數值,return 出來讓數值可以去 setter,下方為更改過後的程式碼: :::

const { ref, reactive, watch } = Vue;
const App = {
  setup() {
    const num1 = reactive({ idx: 0 });

    watch(
      () => num1.idx,
      (n, o) => {
        console.log(`new: ${n} ; old: ${o}`);
      }
    );
    setInterval(() => {
      num1.idx++;
    }, 2000);

    return {
      num1,
    };
  },
};

Vue.createApp(App).mount("#app");

ref & reactive 物件監聽差異

當今天 ref & reactive 兩者都為物件型別,在 watch 時,會發生一些不同。

ref 物件型別

是無法直接進行深層監聽的,需要使用其他的方法來處理, 也就是當今天 ref 裡頭有個 index:0 的數值,如果我只有觀察 num 整個 ref 的話,是無法被成功監聽的

const { ref, reactive, watch } = Vue;

const App = {
  setup() {
    const num = ref({ idx: 0 });

    watch(num, (n, o) => {
      console.log(`new: ${n} ; old: ${o}`);
    });

    setTimeout(() => {
      num.value.idx++;
    }, 2000);

    return {
      num,
    };
  },
};

Vue.createApp(App).mount("#app");

ref 其實是可以觀察物件型別的,只是不能監控 ref 整個物件的變動,如果僅需要觀察物件為一個數值的話,可以使用下方的寫法:

const { ref, reactive, watch } = Vue;

const App = {
  setup() {
    const refObj = ref({ idx: 0 });

    watch(
      ()=> refObj.value.idx,
      (n, o) => {
      console.log(`new: ${n} ; old: ${o}`);
    });

    setTimeout(() => {
      refObj.value.idx++;
    }, 2000);

    return {
      refObj,
    };
  },
};

Vue.createApp(App).mount("#app");

如果真的必須得監控整個 ref 物件時,可以新增 deep:true 的屬性設定,但千萬要記得盡可能避免此寫法,因為瀏覽器是需要耗費較多的效能,去持續一個一個監聽數值被改變過後的樣子。

reactive 物件型別

如果為物件型別,可以透過函示去將觀察的數值回傳出來即可,成功解決

const { ref, reactive, watch } = Vue;

const App = {
  setup() {
    const num = ref({ idx: 0 });
    const num1 = reactive({ idx: 0 });

    watch(num, (n, o) => {
      console.log(`new: ${n} ; old: ${o}`);
    });
    watch(
      () => num1.idx,
      (n, o) => {
        console.log(`new: ${n} ; old: ${o}`);
      }
    );

    setTimeout(() => {
      num.value.idx++;
      num1.idx++;
    }, 2000);

    return {
      num,
      num1,
    };
  },
};

Vue.createApp(App).mount("#app");

watchEffect

跟 watch 方法一樣,是在監聽數值的變更,只是有一些不同的使用方式以及特性

首先,watchEffect 是不需要傳入任何所需要監控的數值,直接使用 callback 函示即可,並且在讀取或是執行需要觀察的數值,即可自動加入監聽該數值,例如下方這段基礎的起手式:

const num = ref(0);

watchEffect(() => {
  console.log(num.value)
});

:::tip watchEffect 與 watch 最大的差異為:

  • watch 當所監控數值有變化時,才會去觸發。
  • watchEffect 則是畫面在 mounted 完成時,就會先執行一次。

:::

stop()

當今天如果有需要監聽事件,在變化到一個數值,停止監聽時,可以使用 stop(),這個 stop 是可以自定義名稱的,但建議用比較直覺性的名稱來表示,例如我這裡使用的名稱為 stop

const num = ref(0)

const stop = watchEffect(() => {
  if (num.value >= 4) {
    stop();
  }
});

setInterval(() => {
  num.value++;
}, 1000);

:::warning 這邊也再次強調,雖然我們透過 devtool 可以發現在 console 只有到 4 就沒有再繼續 console 了, 但實際上 stop() 只是關閉了整個 watchEffect() ,範例中下方的 setInterval() 依然在持續進行的! :::

結論

ref 以及 reactive 在 watch 的使用上會稍微的不一樣,所以要去看是否有無要 watch 數值,去選擇要使用 ref or reactive。

13 views

More from this blog

Vue3-06-props&emit 元件的溝通傳遞

元件的溝通傳遞 Vue.js 每個元件的實體狀態、模板等作用範圍都應該要是獨立的, 也就是我們不能(也不應該)在子元件的模組「直接」去修改父元件,甚至是另一個元件的資料,因為元件化就是為了能夠重複使用,希望這些元件可以根據『外部&內部』資料的改變,來呈現出不同的結果, 如果需要透過父層來傳遞資料給子層的話,就是 props 如果需要透過子層來傳遞資料給父層的話,就是 emit ![props&emit 傳遞流程](https://d1dwq032kyr03c.cloudfront.net/u...

Aug 18, 20222 min read31
Vue3-06-props&emit 元件的溝通傳遞

JavaScript - Macrotask 與 MicroTask 差異以及情境使用

提到 Macrotask 與 MicroTask 之前,先再次複習 Promise 的觀念: 一個 Promise 最終會有兩種狀態 對應 Promise 的不同狀態,會各自觸發 .then 與 .catch 兩個函式 利用 Promise 可以達成非同步行為,而且內容可以自訂 Macrotasks 關於 Event Queue 有些 Web API 是具有非同步的行為,而在非同步的目的達成之後,瀏覽器會把給定的對應的函式推送到 Event Queue 裡面,這些一個一個函式正好代表每一件要...

Aug 6, 20222 min read34

JavaScript - Event-Loop

這次在練習一些前端常見的面試提時,遇到了一個有關 Event Loop 的題目問題,實在不是很暸解確切的執行過程,因此記錄一下學習筆記。 JavaScript - Event-Loop 關於 Event-Loop 在瀏覽器執行時的優點有兩項: Event Loop 協助了非同步請求的實現,並產生連貫的畫面呈現(influent UI)。 ![同步請求(Synchronous) v.s. 非同步請求(Asynchronous) (from: Alpha Camp)](https://miro.m...

Aug 5, 20222 min read29

Untitled Publication

79 posts