💡 背景脈絡與核心思維

在 React 生態中,開發者被嚴格要求遵守「不可變性 (Immutability)」;然而在 Vue 的世界裡,框架的核心設計就是擁抱「直接修改 (Direct Mutation)」。

雖然 Vue 賦予了我們極大的開發便利性,但「框架允許你這麼做」並不代表「架構上你應該到處這麼做」。當我們在處理 外部 Props全域 Store,甚至是 非同步的迴圈操作 時,毫無節制的直接修改往往會成為專案中難以追蹤的技術債。本文將從 Vue 2 與 Vue 3 的底層差異出發,全面剖析這三大危險邊界。

1. Vue 的心智模型:底層攔截機制的演進

Vue 為什麼推崇直接修改?因為它在底層幫你做掉了「依賴追蹤」與「畫面更新」的髒活。但 Vue 2 與 Vue 3 在攔截機制上有著本質的區別:

詳細閱讀:📘 Vue2 與 Vue3 底層差異:defineProperty 與 原生的 Proxy


2. 第一道危險邊界:Computed 的參照陷阱 (The Computed Reference Trap)

這是許多資深開發者也會踩中的地雷。雖然 computed 本身是唯讀的(除非定義了 Setter),但當它回傳一個物件陣列時,JavaScript 的參照特性會繞過 Vue 的唯讀保護。

🚨 災難場景 A:短暫的幻覺(衍生新物件)

當 Computed 根據某些狀態回傳一個全新物件:

// Vue 3 寫法示範
const price = ref(100);
const qty = ref(2);

const orderInfo = computed(() => {
  return {
    total: price.value * qty.value,
    discount: 0
  };
});

如果你執行 orderInfo.value.discount = 50

🚨 災難場景 B:走後門的偷渡(直連 Proxy 參照)

當 Computed 直接回傳一個現有的響應式物件:

// 將全域 Store 的物件透過 computed 曝露給 Template 用
const userStore = useUserStore();
const currentUser = computed(() => userStore.profile);

執行 currentUser.value.name = 'Parker'