本文 AI 產出,尚未審核
Vue3 Composition API:computed 的 Getter / Setter 實作與應用
簡介
在 Vue 3 中,computed 不僅是一個只讀的衍生屬性,還可以同時提供 getter 與 setter,讓你在 UI 與資料之間建立雙向的映射關係。
透過 Composition API 的 computed(() => ...) 形式,我們可以把複雜的資料轉換、驗證、或同步邏輯封裝在同一個可觀測的變數裡,讓組件的 可讀性 與 可維護性 大幅提升。
本篇文章將帶你從概念說明、實作範例,到常見陷阱與最佳實踐,完整掌握 computed getter / setter 的使用方式,並提供幾個在實務開發中常見的應用情境。
核心概念
1. 為什麼需要 setter?
- 雙向綁定:在使用
<input v-model="fullName">時,若fullName是一個計算屬性,Vue 會自動呼叫其 setter 來更新底層資料。 - 資料驗證:在 setter 中加入驗證或格式化邏輯,可避免不合法的值寫入原始資料。
- 同步多個來源:當一個 UI 元件需要同時影響多個 state 時,setter 可以一次性完成同步,避免多次觸發重繪。
2. computed 的基本語法
import { ref, computed } from 'vue'
const count = ref(0)
// 只讀 computed
const double = computed(() => count.value * 2)
若要同時提供 getter 與 setter,必須傳入一個 物件,物件裡包含 get 與 set 兩個函式:
const fullName = computed({
get() {
// 讀取時的邏輯
},
set(value) {
// 寫入時的邏輯
}
})
3. Getter / Setter 的執行時機
| 時機 | 觸發方式 | 執行的函式 |
|---|---|---|
讀取 computed 的 .value(或在模板中直接使用) |
getter 被呼叫,回傳衍生值 | get() |
為 computed 設定新值(如 v-model) |
setter 被呼叫,接收新值 | set(newValue) |
依賴的 ref 或 reactive 變更 |
getter 重新計算 | get() 再次執行 |
⚠️ 注意:setter 只會在外部「寫入」時被呼叫,內部自行修改依賴的
ref不會觸發 setter。
4. 範例一:姓名分拆與合併
import { ref, computed } from 'vue'
export default {
setup() {
const firstName = ref('王')
const lastName = ref('小明')
// fullName 同時提供 getter 與 setter
const fullName = computed({
// 讀取時把姓與名組合成「王 小明」
get() {
return `${firstName.value} ${lastName.value}`
},
// 寫入時把傳入的字串切割回姓與名
set(value) {
const [first, ...rest] = value.split(' ')
firstName.value = first
lastName.value = rest.join(' ')
}
})
return { firstName, lastName, fullName }
}
}
說明:在表單上使用
<input v-model="fullName">,使用者編輯「王 小明」時,setter 會自動把字串拆回firstName、lastName,實現 雙向綁定。
5. 範例二:價格與稅金的雙向計算
import { ref, computed } from 'vue'
export default {
setup() {
const price = ref(1000) // 稅前價格
const taxRate = ref(0.05) // 稅率 5%
// total 包含稅金,既能讀也能寫
const total = computed({
get() {
return price.value * (1 + taxRate.value)
},
set(value) {
// 使用者直接輸入含稅金額,我們反推稅前價格
price.value = value / (1 + taxRate.value)
}
})
return { price, taxRate, total }
}
}
說明:當 UI 允許使用者直接輸入「含稅金額」時,setter 會自動把金額轉回「稅前價格」,避免在其他地方重複寫反推邏輯。
6. 範例三:表單欄位的即時驗證
import { ref, computed } from 'vue'
export default {
setup() {
const rawAge = ref('') // 使用者輸入的字串
const age = computed({
get() {
// 只要是合法的正整數就回傳 Number,否則回傳 null
const n = Number(rawAge.value)
return Number.isInteger(n) && n > 0 ? n : null
},
set(value) {
// 只接受正整數,其他值直接忽略
if (Number.isInteger(value) && value > 0) {
rawAge.value = String(value)
}
}
})
// 用於 UI 顯示錯誤訊息
const ageError = computed(() => {
return rawAge.value !== '' && age.value === null
? '年齡必須是正整數'
: ''
})
return { rawAge, age, ageError }
}
}
說明:透過 setter 的驗證,我們保證
age永遠是 Number 或 null,而 UI 只需要監看ageError即可顯示即時錯誤訊息。
常見陷阱與最佳實踐
| 陷阱 | 可能的後果 | 解決方案 |
|---|---|---|
| 在 setter 中直接改寫同一個 computed | 造成無限遞迴或 Stack Overflow | 永遠 改寫其 依賴的 ref 或 reactive,不要在 setter 內部再次賦值給自己。 |
忘記返回值(getter 沒有 return) |
computed 永遠回傳 undefined,導致 UI 顯示錯誤 |
確認 get() 函式最後有 return,或使用箭頭函式直接回傳。 |
| 在 getter 中執行副作用(如 API 呼叫) | 每次依賴變更都會觸發副作用,造成效能與重複請求問題 | 副作用應放在 watch 或 watchEffect,保持 getter 純函式。 |
使用 ref 包裝已是 computed 的值 |
產生不必要的嵌套,讀取時需 .value.value |
只在需要時才使用 ref,不要把 computed 再包一層 ref。 |
忘記在模板中使用 .value(Composition API) |
顯示空白或錯誤訊息 | 在 <script setup> 中直接使用變數,Vue 會自動解包;在普通 setup 返回時,模板仍需 .value。 |
最佳實踐
- 保持 getter 純粹:只做計算與回傳,不修改任何 state。
- 在 setter 中集中所有寫入邏輯:包括驗證、格式化、同步其他欄位。
- 使用 TypeScript 時明確標註型別,例如
computed<string>,可以提前捕捉錯誤。 - 對於需要多個依賴的 computed,盡量拆成小的 computed 再組合,提升可讀性與重用性。
- 在大型表單或資料同步情境下,搭配
watch監控 computed 的變化,以執行額外的副作用(如 API 更新)。
實際應用場景
| 場景 | 為什麼使用 computed getter/setter |
|---|---|
| 表單欄位聯動(例如「地址」分為縣市與郵遞區號) | 使用 getter 合併顯示,setter 解析回各自的 ref,保持 UI 與資料同步。 |
| 金額顯示與編輯(含稅 / 未稅) | 讓使用者自由切換編輯模式,setter 自動把輸入的金額轉換為內部統一的基礎值。 |
| 多語系文字切換 | getter 根據當前語系返回對應文字,setter 可在需要時改變語系(如切換語系的 UI 控制)。 |
| 即時驗證與格式化(電話號碼、信用卡號) | setter 只接受符合格式的值,getter 回傳已格式化的字串,減少 UI 重複處理。 |
| 資料視圖層的投影(只顯示部分屬性) | 在列表頁只需要顯示 fullName,在編輯頁使用相同 computed 的 setter 直接寫回。 |
總結
computed的 getter / setter 為 Vue 3 Composition API 提供了 雙向計算屬性 的能力,是實作 v-model、表單同步、即時驗證 等需求的關鍵工具。- getter 必須保持純粹,只負責計算與回傳;setter 則是集中寫入、驗證與同步的地方。
- 正確使用可以讓程式碼更簡潔、可讀、可維護,同時避免不必要的效能問題。
- 在開發過程中留意常見陷阱(遞迴、在 getter 中副作用)並遵守最佳實踐,能讓你的 Vue 3 專案更穩定、易於擴充。
掌握了 computed getter / setter,你就能在 Vue 3 中以更精緻的方式管理資料與 UI 之間的互動,寫出既直觀又強大的應用程式。祝開發順利!