Try   HackMD

2023/08/11

Vue 的響應式基礎

Vue 是一個前端框架,它可以讓我們用簡單的方式開發出動態的網頁。學習使用 Vue 的一大重點就是它的響應式,也就是說當使用者在網頁改變了資料,網頁畫面也會自動更新。這篇文章將介紹什麼是響應式,以及如何使用 Vue 提供的 ref()reactive() 函數來定義響應式的數據。

什麼是響應式的值? 與一般數值的不同?

在 JavaScript 中,我們可以用各種類型的值來儲存資料,例如數字、字串、陣列、物件等。這些值都是可變的,也就是說,我們可以隨時修改它們的內容。例如:

let x = 1; // 定義一個數字
x = x + 1; // 修改數字
console.log(x); // 顯示 2

let y = [1, 2, 3]; // 定義一個陣列
y.push(4); // 修改陣列
console.log(y); // 顯示 [1, 2, 3, 4]

但是,這些值並不是響應式的,也就是說,當我們修改了它們,並不會觸發任何其他的動作。例如,如果我們想要在網頁上顯示這些值,我們必須手動更新畫面。例如:

<div id="app">
  <p>x = {{ x }}</p>
  <p>y = {{ y }}</p>
</div>
<script>
  let x = 1;
  let y = [1, 2, 3];
  const app = Vue.createApp({
    data() {
      return {
        x: x,
        y: y,
      };
    },
  });
  app.mount("#app");
</script>

這段程式碼會在網頁上顯示 x = 1y = [1, 2, 3]。但是,如果我們在 console 中執行 x = x + 1y.push(4),網頁上的內容並不會改變。這是因為 xy 只是普通的 JavaScript 值,它們並沒有和 Vue 的資料產生關聯。

要讓 Vue 的資料和畫面產生關聯,我們必須使用 Vue 提供的響應式的值。響應式的值和普通的值有以下幾點不同:

  • 響應式的值是由 Vue 內部管理的,它們有一些特殊的屬性和方法。
  • 響應式的值可以被 Vue 的資料或其他響應式的值追蹤和監聽,當它們改變時,會觸發相關的更新。
  • 響應式的值可以被 Vue 的模板或其他函數使用,當它們改變時,會自動更新畫面或執行其他動作。

什麼情況會失去響應?

雖然 Vue 提供了方便的方式來創建和使用響應式的值,但是我們仍然要注意一些可能導致響應式失效的情況。以下是一些常見的例子:

  • 如果我們直接修改了一個物件或陣列的屬性或索引,而不是使用 Vue 提供的方法,則 Vue 可能無法偵測到變化。例如:
const obj = reactive({ a: 1 }); // 定義一個響應式的物件
obj.b = 2; // 直接新增一個屬性
console.log(obj.b); // 顯示 2
watch(
  () => obj.b,
  (newValue, oldValue) => {
    console.log(`obj.b changed from ${oldValue} to ${newValue}`);
  }
); // 定義一個監聽器
obj.b = 3; // 直接修改屬性
// 預期會顯示 obj.b changed from 2 to 3,但實際上沒有任何輸出

這是因為 Vue 在創建響應式的物件時,只會對已經存在的屬性進行追蹤,如果我們後來新增了新的屬性,Vue 就無法知道它的變化。要解決這個問題,我們可以使用 Vue.set(obj, 'b', 2)obj = { ...obj, b: 2 } 來新增屬性,這樣 Vue 就可以正確地更新響應式。

  • 如果我們使用了一些非響應式的值來定義響應式的值,則 Vue 只會追蹤響應式的值本身,而不會追蹤非響應式的值。例如:
let x = 1; // 定義一個非響應式的數字
const y = ref(x); // 定義一個響應式的數字
watch(
  () => y.value,
  (newValue, oldValue) => {
    console.log(`y changed from ${oldValue} to ${newValue}`);
  }
); // 定義一個監聽器
x = x + 1; // 修改非響應式的數字
// 預期會顯示 y changed from 1 to 2,但實際上沒有任何輸出

這是因為 Vue 在創建 y 時,只會把 x 的值複製過來,而不會建立任何關聯。如果我們後來修改了 x 的值,Vue 就無法知道它的變化。要解決這個問題,我們可以使用 computed() 函數來定義 y,這樣 Vue 就可以正確地更新響應式。例如:

let x = 1; // 定義一個非響應式的數字
const y = computed(() => x); // 定義一個響應式的數字
watch(
  () => y.value,
  (newValue, oldValue) => {
    console.log(`y changed from ${oldValue} to ${newValue}`);
  }
); // 定義一個監聽器
x = x + 1; // 修改非響應式的數字
// 正確地顯示 y changed from 1 to 2

創建響應式數值的函數: ref()、reactive()

要創建和使用響應式的值,Vue 提供了兩個主要的函數:ref()reactive()。這兩個函數都可以把普通的值轉換成響應式的值,但是它們有一些不同的用法和特點,我們來看看它們的差異和適用的情況。

ref()

ref() 函數可以把一個基本類型的值(例如數字、字串、布林等)轉換成一個響應式的物件,這個物件有一個 value 屬性,用來存放原始的值。例如:

const x = ref(1); // 定義一個響應式的數字
console.log(x.value); // 顯示 1
x.value = x.value + 1; // 修改數字
console.log(x.value); // 顯示 2

reactive()

reactive() 函數可以把一個複雜類型的值(例如物件、陣列、Map、Set等)轉換成一個響應式的物件,這個物件會保留原始的結構和方法,但是它的屬性或元素都會變成響應式的。例如:

const y = reactive([1, 2, 3]); // 定義一個響應式的陣列
console.log(y[0]); // 顯示 1
y[0] = y[0] + 1; // 修改陣列
console.log(y[0]); // 顯示 2

如何選擇使用?

ref()reactive() 都可以用來定義響應式的值,但是它們有一些不同的使用場景和注意事項。以下是一些常見的規則:

  • 如果我們要定義的值是一個基本類型,則建議使用 ref(),因為這樣可以避免在使用時忘記加上 .value
  • 如果我們要定義的值是一個複雜類型,則建議使用 reactive(),因為這樣可以保持原始的結構和方法,而不需要對每個屬性或元素都使用 ref()
  • 如果我們要定義的值是一個常數,則建議使用 readonly() 函數來包裝 ref()reactive(),這樣可以避免意外地修改它們。
  • 如果我們要定義的值是一個計算出來的值,則建議使用 computed() 函數來包裝 ref()reactive(),這樣可以讓它們根據其他響應式的值自動更新。

小結

Vue 的響應式是一個強大而方便的特性,它可以讓我們用簡單的方式開發出動態的網頁。在 Vue 中,我們可以使用 ref()reactive() 函數來定義和使用響應式的值。這兩個函數都可以把普通的值轉換成響應式的值,但是它們有一些不同的用法和特點。我們要根據不同的情況選擇合適的函數,並且注意一些可能導致響應式失效的情況。這樣,我們就可以充分利用 Vue 的響應式,開發出高效而美觀的網頁。

參考