computed(計算屬性)并不是vue獨創的,而是源自計算機科學和響應式編程的長期發展
計算理論的奠基:
- 函數式編程的純函數思想:計算屬性的核心特征(無副作用、依賴輸入確定輸出)直接來源于函數式編程中的純函數概念
- 響應式編程的衍生值:在響應式系統中,任何可以依據其他的“可觀察量”自動計算出的值都可以視為計算屬性
vue中computed的演進
vue 1.x時代:(2014-2016)
初步實現:通過 computed 選項定義計算屬性,基于依賴收集的響應式系統
特點:
- 基于Object.defineProperty實現響應式
- 計算屬性會被混入到Vue實例中,像普通屬性一樣訪問
- 已具備緩存機制,依賴不變時直接返回緩存值
Vue 2.x 時代:(2016-2020)
優化改進:
- 計算屬性的惰性求值:只在真正被訪問時才計算
- 更精細的依賴追蹤:基于 Watcher 類的依賴收集系統
- 支持 setter:允許通過賦值反向影響依賴項3
computed: {fullName: {get() { return this.firstName + ' ' + this.lastName },set(newValue) { /* 處理賦值邏輯 */ }}
}
Vue 3.x 時代(2020至今)
組合式 API 重構:
- 引入 computed() 函數,可在 setup() 或 在 < script setup > 中使用
- 基于 Proxy 的響應式系統,解決 Vue 2 的檢測限制
- 與 Reactivity System 深度集成,性能顯著提升
import { computed } from 'vue'
const count = ref(1)
const double = computed(() => count.value * 2)
computed設計核心原理:
1、響應式依賴追蹤
- 自動收集依賴:執行計算屬性時候,訪問的每個屬性都會被記錄依賴
- 更新觸發:當任何屬性變化時候,標記計算屬性為“臟”dirty, 下次訪問時候再重新計算
2、緩存機制的實現
// 簡化的 computed 實現原理
function computed(getter) {let valuelet dirty = trueconst runner = effect(getter, {lazy: true,scheduler: () => {dirty = true // 依賴變化時標記為需要重新計算trigger(obj, 'value') // 觸發依賴此計算屬性的副作用}})const obj = {get value() {if (dirty) {value = runner() // 執行 getter 計算新值dirty = false}return value}}return obj
}
在vue中 同步派生狀態指的是基于現有響應式數據立即計算得出的新狀態,這種計算是同步完成的,具有即時性和準確性,這是computed計算屬性的核心設計理念
同步派生狀態的核心特征
1、即時計算:當依賴變化時候,派生值同步(立即)重新計算
const count = ref(1);
const double = computed(() => count.value * 2); // 同步計算
console.log(double.value); // 2
count.value = 3;
console.log(double.value); // 6 (立即更新)
2、純函數特性:派生過程不允許修改外部狀態,僅依賴輸入:
// 純派生:結果僅取決于 count.value
const triple = computed(() => count.value * 3);
3、與異步派生對比
特性 | 同步派生 (computed) | 異步派生 (如 watch + 狀態) |
---|---|---|
執行時機 | 立即完成 | 延遲完成(需等待 Promise) |
模板引用 | 可直接使用 | 需要處理加載中/錯誤狀態 |
響應式追蹤 | 自動追蹤依賴 | 需手動管理依賴 |
返回值 | 必須返回 | 無返回值(通過修改狀態輸出) |
需要每次執行的邏輯
1、模板渲染的即時性需求:vue模板渲染需要同步獲取值進行渲染,異步派生會導致渲染中斷或需要額外的處理加載狀態
2、響應式系統的設計基礎:vue的響應式依賴追蹤依賴同步訪問
3、緩存優化前提:同步計算使得緩存機制能精準判斷是否需要重新計算
同步派生使用場景:
// 數據格式化
const price = ref(100);
const formattedPrice = computed(() => `¥${price.value.toFixed(2)}`);// 過濾/篩選列表
const todos = ref([/*...*/]);
const activeTodos = computed(() => todos.value.filter(todo => !todo.completed)
);// 多狀態組合
const width = ref(10);
const height = ref(20);
const area = computed(() => width.value * height.value);
同步派生的優勢:
- 性能高效:基于依賴追蹤的精準緩存
- 心智模型簡單:輸入輸出關系明確
- 調試友好:無隱藏的異步時序問題
- 組合方便:可安全地被其他計算屬性引用
vue中的“同步派生狀態”是通過computed來實現的即時性、確定性的數據轉換,它是vue響應式系統的核心支柱,這種設計確保了:
- 模版渲染的即時性、可靠性
- 狀態變化的可預測性
- 派生邏輯的高效性
computed為什么會有緩存機制:
vue中的computed采用緩存機制主要是為了優化性能和保證一致性:當計算屬性依賴性沒有變化時,vue會直接返回上一次的計算結果,而不會重新計算
緩存優勢:
1、性能優化:
- 避免重復計算:模版中多次引用同一個計算屬性時,依賴未變化就不需要重新計算;如果沒有緩存,expensiveCalc 則會在每次渲染時候都需要計算
<template><div>{{ expensiveCalc }}</div> <!-- 計算一次 --><div>{{ expensiveCalc }}</div> <!-- 直接讀緩存 -->
</template>
- 減少渲染開銷: vue的渲染更新是批量的,緩存機制可以避免同一輪更新中多次觸發相同的計算
2、保證一致性
- 同步派生狀態的確定性:在同一個事件循環中,無論訪問多少次計算屬性,其結果都是相同
- 避免副作用污染:計算屬性時純函數,緩存機制強調開發者遵守這一原則, 無法在計算屬性中執行副作用
3、與響應式系統的協同
- 精準依賴追蹤:緩存機制依賴vue的響應式系統,只有當確定的依賴項變化時候才需要重新計算
緩存實現的關鍵技術
1、惰性計算:只有在真正訪問屬性時才會計算,如果依賴性沒有變化則直接讀取緩存值
2、標記-清除機制:“臟”(dirty)狀態標記:當依賴性發生變化時,標記屬性為“dirty”. 下次訪問時候再重新計算
// 偽代碼示意Vue內部邏輯
class ComputedRef {constructor(getter) {this._dirty = true; // 初始標記為需要計算this._value = undefined;}get value() {if (this._dirty) {this._value = getter(); // 重新計算this._dirty = false;}return this._value;}
}
3、依賴收集的精準性
- 計算屬性執行時會自動追蹤所有被訪問的響應式變量
- 只有這些被追蹤的變量變化時才會觸發重新計算
何時會打破緩存:
- 依賴項發生變化(主動觸發):
const a = ref(1);
const b = computed(() => a.value * 2);
a.value = 2; // 修改依賴,下次訪問b時重新計算
- 手動強制刷新(特殊情況)
// 部分場景可通過賦值觸發(不推薦)
b.value = 5; // 如果計算屬性可寫(setter)
與方法的對比:
特性 | computed (帶緩存) | 方法 (無緩存) |
---|---|---|
執行時機 | 依賴變化時 | 每次調用都執行 |
性能 | 高效(緩存結果) | 可能重復計算 |
模板使用 | 自動優化 | 需自行優化(如 v-once) |
適用場景 | 純計算、派生狀態 | 需要每次執行的邏輯 |