1️⃣ 核心区别表
| 特性 | computed | watch |
|---|---|---|
| 用途 | 计算衍生数据 | 监听变化执行副作用 |
| 返回值 | 会返回值并缓存 | 无返回值(主要触发回调) |
| 执行时机 | 访问 .value 时才执行 |
数据变化时立即触发(flush 可控制) |
| 缓存机制 | ✅ 会缓存,依赖未变不重新计算 | ❌ 不缓存,每次依赖变化都会触发回调 |
| 依赖收集 | 自动收集依赖 | 需手动指定监听对象(或 watchEffect 自动收集) |
| 可获取 oldValue | ❌ | ✅ |
| 异步支持 | ❌ 仅同步计算 | ✅ 可做异步副作用(API、DOM 操作等) |
| 适合场景 | 纯逻辑计算、UI 展示数据 | 副作用逻辑、异步请求、操作 DOM 等 |
2️⃣ 理解方式
-
computed = 计算属性(惰性求值 + 缓存)
- 主要用于根据已有数据计算出新的数据
- 访问 value 时才触发计算
- 不适合做副作用
-
watch = 数据侦听器(观察者模式)
- 用于监听某个响应式变量变化
- 触发回调执行副作用(API 请求、DOM 操作、日志等)
- 可访问新旧值
- 可以做异步操作
3️⃣ 原理上的差异
-
computed
- 内部有
effect+dirtyflag - 依赖变化时标记 dirty = true
- 下一次访问 value 时重新计算
- 可以缓存,减少重复计算
- 内部有
-
watch
- 直接对响应式数据注册 effect
- 数据变化时立即触发回调
- 回调不返回值
- 可选择 flush:
pre/post/sync控制执行时机
computed 的原理
computed 本质上是一个 带缓存的响应式副作用:
-
依赖收集
- 当访问
computed.value时,Vue 会触发其内部effect,追踪它依赖的响应式数据(ref 或 reactive 对象)。
- 当访问
-
惰性求值(lazy)
- 初次访问时执行 getter,生成值并缓存
- 标记
dirty = false
-
缓存机制
- 当依赖变化时,响应式系统会将
dirty = true - 下次访问
.value时重新计算并更新缓存
- 当依赖变化时,响应式系统会将
-
不可获取旧值
- 因为 computed 只关注最新值,内部只存当前缓存
示意流程:
ini
访问 computed.value → 检查 dirty
├─ dirty = true → 执行 getter → 缓存值 → dirty = false
└─ dirty = false → 直接返回缓存值
依赖变化 → effect 标记 dirty = true
示例代码:
arduino
import { ref, computed } from 'vue'
const count = ref(1)
const double = computed(() => count.value * 2)
console.log(double.value) // 2
count.value++
console.log(double.value) // 4,重新计算
2️⃣ watch 的原理
watch 本质是一个 响应式数据的观察者(Watcher) ,用于执行副作用:
-
手动依赖选择
- 你传入要监听的响应式对象(ref / reactive / getter)
- Vue 内部会对其注册 effect
-
响应式触发
- 当被监听的数据发生变化时,effect 会触发回调函数
-
可获取 oldValue / newValue
- watch 在触发时会提供新值和旧值
-
异步可选
- 默认 flush: 'pre'
- 可以指定 'post'(DOM 更新后)或 'sync'(同步执行)
-
没有缓存
- 每次数据变化都会调用回调函数
- 适合副作用逻辑,不用于纯计算
示意流程:
scss
watch(reactiveData, callback)
└─ 内部创建 effect
依赖变化 → 触发 effect → callback(newValue, oldValue)
示例代码:
javascript
import { ref, watch } from 'vue'
const count = ref(1)
watch(count, (newVal, oldVal) => {
console.log(`count changed from ${oldVal} to ${newVal}`)
})
count.value++ // 触发 watch 回调
3️⃣ 原理总结对比
| 特性 | computed | watch |
|---|---|---|
| 内部机制 | effect + dirty flag | effect 直接触发回调 |
| 触发时机 | 访问 .value 时(懒执行) |
数据变化时立即触发 |
| 缓存 | ✅ 缓存上次计算值 | ❌ 不缓存,每次触发回调 |
| oldValue | ❌ 无 | ✅ 有 |
| 使用目的 | 衍生数据计算 | 执行副作用(异步、DOM 操作等) |