Skip to content

Vue 3 核心教學:掌握響應式基礎與資料驅動核心

在前面幾篇教學中,我們反覆提到了一個詞:資料驅動畫面。當 JavaScript 中的資料改變時,HTML 畫面就會魔術般地自動更新。

這個魔術在 Vue 3 中,主要是透過 Composition API 的 ref()reactive() 兩個核心方法來實現的。本文將帶您深入這兩個方法的底層邏輯,讓您不再為 .value 的使用時機感到困惑,並能正確選用適合的響應式武器。

為什麼需要響應式?直擊 Vue 3 的底層秘密

在傳統的 JavaScript 中,如果您修改了一個變數,瀏覽器是不會主動幫您更新網頁畫面的。您必須自己動手寫 document.getElementById().innerText = newValue

Vue 3 則是利用了 ES6 的 Proxy(代理) 機制。當您把一個普通的資料丟進 Vue 的響應式系統時,Vue 會在外面包一層 監聽器。只要您對資料進行讀取或修改,監聽器就會立刻察覺,並通知網頁去重新渲染對應的區塊。

為了滿足不同資料型態的監聽需求,Vue 3 提供了兩種主要方式:ref()reactive()

單一值的守護者:ref

ref() 是 Vue 3 最常用且最萬能的響應式宣告方式。

適用資料型態

ref() 可以接收任何資料型態,包含:

  • 基本型態:String, Number, Boolean, null, undefined。
  • 複合型態:Object, Array。

核心特點:value 的語法糖

因為 JavaScript 的基本型態,例如:數字或字串是傳值而非傳參照,Vue 無法直接監聽它們的變化。因此,ref() 會將您傳入的值包裝成一個 特殊的物件 RefImpl,並把真實的資料存放在 value 屬性中。

在 JavaScript

必須加上 .value 才能讀取或修改資料。

js
const price = ref(100);
const isVisible = ref(false);
const username = ref("");

// 數字運算
price.value = price.value * 0.9; // 打九折

// 布林切換
isVisible.value = !isVisible.value;

// 字串操作
username.value = username.value.trim().toUpperCase();

在 HTML 模板

Vue 會自動幫您 解包(Unwrap),因此絕對不要加 .value

html
<!-- 模板中不加 .value -->
<p>{{ count }}</p>
<p>{{ name }}</p>

物件與陣列的專屬管家:reactive

相較於 ref() 的萬能,reactive() 則是專門用來處理非基本型態(物件或陣列)的響應式方法。

適用資料型態

只能接收物件或陣列。如果您嘗試把數字或字串丟進 reactive(),Vue 會在控制台跳出警告。

核心特點:免用 .value

因為 reactive() 直接將物件透過 Proxy 進行深度代理,所以不論在 JavaScript 還是 HTML 模板中,您都完全不需要寫 .value,操作起來就跟原生 JavaScript 物件一模一樣。

陣列操作

所有原生陣列方法都可以直接用,且都會觸發反應式更新:

js
const items = reactive(["html", "css", "javascript"]);

items.push("vue"); // 尾端新增
items.unshift("http"); // 頭端新增
items.pop(); // 移除最後一個
items.shift(); // 移除第一個
items.splice(1, 1); // 移除索引 1 的元素
items[0] = "HTML5"; // 直接修改索引(Vue 3 支援)
items.sort(); // 排序
items.reverse(); // 反轉

物件操作

js
const user = reactive({
  name: "小明",
  age: 18,
  email: "ming@example.com",
});

// 讀取
console.log(user.name); // '小明'
console.log(user.age); // 18

// 修改屬性
user.name = "小華";
user.age++;

// 新增屬性(Vue 3 支援,Vue 2 不支援)
user.phone = "0912-345-678";

開發大抉擇:ref 與 reactive 的關鍵對比與地雷

既然兩者都能處理物件,開發時該怎麼選?以下是初學者必知的三大核心差異與隱藏地雷:

核心特性快速對比表

特性refreactive
可接受型態任何型態(基本與複合型態皆可)僅限物件、陣列、Map/Set
JS 中讀寫必須使用 .value直接讀寫,免用 .value
HTML 模板自動解包,直接使用直接使用
底層機制包裝成物件 RefImpl透過 ES6 Proxy 代理

reactive 的致命地雷:失去響應性

雖然 reactive() 少了 .value 看起來很優雅,但它隱藏了兩個非常容易讓網頁畫面卡死更新的地雷:

重新賦值

如果您直接把一個新物件指派給 reactive 變數,舊的 Proxy 參照會被切斷,Vue 將徹底失去對它的追蹤!

js
let state = reactive({ count: 0 });

// 這樣做會讓 state 失去響應性!畫面再也不會變了
state = { count: 99 };

若需要重新賦值,用 Object.assign()

js
const user = reactive({ name: "小明" });

Object.assign(user, { name: "小華", age: 20 });

解構賦值

直接解構 reactive 物件,拿出來的變數會變成普通的純值,同樣失去響應性。

js
const state = reactive({ count: 0 });
// 拿出來的 count 只是個普通的數字 0,修改它不會觸發畫面更新
let { count } = state;

若需要解構,用 toRefs()

js
const { reactive, toRefs } = Vue;
const user = reactive({ name: "小明", age: 18 });
const { name } = user; // 解構後 name 失去響應式

// 若需要解構,用 toRefs
const { name } = toRefs(user);

如何避開地雷?

  • 如果用 reactive 想重新賦值,必須改成修改其內部屬性,例如:state.data = newData
  • 如果使用 ref 處理物件,例如:const state = ref({ count: 0 }),重新賦值只需要 state.value = { count: 99 },完全不會有失去響應性的風險!