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 才能讀取或修改資料。
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。
<!-- 模板中不加 .value -->
<p>{{ count }}</p>
<p>{{ name }}</p>物件與陣列的專屬管家:reactive
相較於 ref() 的萬能,reactive() 則是專門用來處理非基本型態(物件或陣列)的響應式方法。
適用資料型態
只能接收物件或陣列。如果您嘗試把數字或字串丟進 reactive(),Vue 會在控制台跳出警告。
核心特點:免用 .value
因為 reactive() 直接將物件透過 Proxy 進行深度代理,所以不論在 JavaScript 還是 HTML 模板中,您都完全不需要寫 .value,操作起來就跟原生 JavaScript 物件一模一樣。
陣列操作
所有原生陣列方法都可以直接用,且都會觸發反應式更新:
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(); // 反轉物件操作
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 的關鍵對比與地雷
既然兩者都能處理物件,開發時該怎麼選?以下是初學者必知的三大核心差異與隱藏地雷:
核心特性快速對比表
| 特性 | ref | reactive |
|---|---|---|
| 可接受型態 | 任何型態(基本與複合型態皆可) | 僅限物件、陣列、Map/Set |
| JS 中讀寫 | 必須使用 .value | 直接讀寫,免用 .value |
| HTML 模板 | 自動解包,直接使用 | 直接使用 |
| 底層機制 | 包裝成物件 RefImpl | 透過 ES6 Proxy 代理 |
reactive 的致命地雷:失去響應性
雖然 reactive() 少了 .value 看起來很優雅,但它隱藏了兩個非常容易讓網頁畫面卡死更新的地雷:
重新賦值
如果您直接把一個新物件指派給 reactive 變數,舊的 Proxy 參照會被切斷,Vue 將徹底失去對它的追蹤!
let state = reactive({ count: 0 });
// 這樣做會讓 state 失去響應性!畫面再也不會變了
state = { count: 99 };若需要重新賦值,用 Object.assign()。
const user = reactive({ name: "小明" });
Object.assign(user, { name: "小華", age: 20 });解構賦值
直接解構 reactive 物件,拿出來的變數會變成普通的純值,同樣失去響應性。
const state = reactive({ count: 0 });
// 拿出來的 count 只是個普通的數字 0,修改它不會觸發畫面更新
let { count } = state;若需要解構,用 toRefs()。
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 },完全不會有失去響應性的風險!
