文章目錄
- 前言
- 🔍 一、`computed` vs `watch`
- ? 示例對比
- 1. `computed` 示例(適合模板綁定、衍生數據)
- 2. `watch` 示例(副作用,如調用接口)
- 🧠 二、源碼實現原理(簡化理解)
- 1. `computed` 原理
- 2. `watch` 原理
- 📌 三、使用建議
- 擴展:
- 🧠 四、Vue 3 響應式系統核心:`effect` / `track` / `trigger`
- 1. `effect(fn)`:響應式副作用收集器
- 2. `track(target, key)`:依賴追蹤
- 3. `trigger(target, key)`:依賴觸發
- 🔁 總結:響應式機制流程
- 🔁 五、`watchEffect` 是什么?
- 🌟 特點:
- 📦 內部工作機制(簡化版)
- 🧪 應用場景對比
- ? 實戰案例:watch vs watchEffect
- `watch` 示例(明確監聽)
- `watchEffect` 示例(更簡潔)
前言
Vue 3 中 computed
和 watch
的區別與源碼實現邏輯(Composition API 版本)。
🔍 一、computed
vs watch
項目 | computed | watch |
---|---|---|
類型 | 派生狀態(緩存) | 響應式副作用 |
用途 | 根據已有響應式變量派生出新數據 | 監聽某個響應式數據的變化后執行副作用邏輯 |
是否緩存 | ? 是 | ? 否 |
返回值 | Ref (值類型) | void (返回值無意義) |
使用場景 | 顯示用、模板綁定 | API 調用、定時器、調試、數據同步等 |
? 示例對比
1. computed
示例(適合模板綁定、衍生數據)
const price = ref(100)
const tax = ref(0.1)const total = computed(() => price.value * (1 + tax.value))
// total.value = 110
2. watch
示例(副作用,如調用接口)
watch(price, (newVal, oldVal) => {console.log(`價格變化:${oldVal} -> ${newVal}`)// 比如向服務器匯報價格變動
})
🧠 二、源碼實現原理(簡化理解)
1. computed
原理
- Vue 內部使用了
effect
與Ref
實現computed
。 - 默認是 lazy effect,只有在訪問
.value
時才執行計算。 - Vue 自動追蹤依賴(依賴收集),只在依賴變化時重新執行。
function computed(getter) {const result = ref()const runner = effect(getter, {lazy: true,scheduler: () => {dirty = true}})return {get value() {if (dirty) {result.value = runner()dirty = false}return result.value}}
}
關鍵點:緩存 + 懶執行 + 自動依賴追蹤
2. watch
原理
watch
是對effect
的進一步封裝。- 內部會遞歸地跟蹤傳入的響應式數據,建立響應關系。
- 依賴變動時,會執行用戶提供的回調函數。
function watch(source, cb) {let oldValue = source()effect(() => {const newValue = source()if (newValue !== oldValue) {cb(newValue, oldValue)oldValue = newValue}})
}
關鍵點:不緩存 + 只執行副作用 + 深層依賴可配置
📌 三、使用建議
需求 | 推薦 |
---|---|
只用來展示(派生數據) | computed |
需要執行副作用(發請求、console.log、同步) | watch |
復雜對象監聽 | watch 配合 deep: true |
多個數據合并監聽 | watch([a, b]) |
擴展:
- 對
watchEffect
的解釋 - 或深入理解 Vue 的響應式核心
effect
、track
、trigger
機制
繼續深入講解 Vue 3 響應式系統的核心:effect
、track
、trigger
,并說明 watchEffect
是如何工作的。
🧠 四、Vue 3 響應式系統核心:effect
/ track
/ trigger
1. effect(fn)
:響應式副作用收集器
- 用于包裹副作用函數(如
computed
的 getter 或watch
的監聽函數)。 - 在函數中讀取響應式數據時,會被自動“追蹤依賴”。
effect(() => {console.log(state.count) // 依賴 `state.count`
})
2. track(target, key)
:依賴追蹤
- 當你讀取響應式對象的屬性時,會觸發
track
。 track
會記錄當前活躍的effect
,讓這個effect
依賴這個屬性。
// 簡化版
function track(target, key) {if (!activeEffect) returnlet depsMap = targetMap.get(target)if (!depsMap) {depsMap = new Map()targetMap.set(target, depsMap)}let deps = depsMap.get(key)if (!deps) {deps = new Set()depsMap.set(key, deps)}deps.add(activeEffect)
}
3. trigger(target, key)
:依賴觸發
- 當你修改響應式數據時,會觸發
trigger
。 - 它會找到依賴這個屬性的所有
effect
,然后重新執行它們。
function trigger(target, key) {const depsMap = targetMap.get(target)if (!depsMap) returnconst effects = depsMap.get(key)effects.forEach(effect => effect())
}
🔁 總結:響應式機制流程
effect(fn)
執行時讀取響應式數據 →track
收集依賴- 數據變化時 →
trigger
調用依賴的effect
→ 重新執行
🔁 五、watchEffect
是什么?
🌟 特點:
- 類似
computed
,但用于副作用。 - 自動收集依賴,無需顯式聲明。
- 依賴變更時自動重新執行副作用函數。
watchEffect(() => {console.log(`count 是 ${count.value}`)
})
📦 內部工作機制(簡化版)
function watchEffect(effectFn) {const runner = effect(effectFn)return () => stop(runner)
}
watchEffect
調用時立即執行effectFn
。- 響應式依賴被收集。
- 當依賴變更時,會重新調用
effectFn
。 - 返回的函數可用于停止監聽。
🧪 應用場景對比
需求場景 | 推薦工具 |
---|---|
計算派生值 | computed |
副作用 + 明確監聽字段 | watch |
副作用 + 自動依賴收集 | watchEffect |
? 實戰案例:watch vs watchEffect
watch
示例(明確監聽)
watch(() => user.id, (newId) => {fetchUserDetail(newId)
})
watchEffect
示例(更簡潔)
watchEffect(() => {fetchUserDetail(user.id)
})