标准答案
- computed 是计算属性,返回一个由响应式数据派生出来的值。它有缓存,依赖不变时重复读取不会重新计算,适合展示金额、过滤列表、组合标题、按钮禁用状态这类派生状态。
- watch 监听一个明确的数据源,可以拿到新值和旧值,适合请求接口、写入缓存、同步 URL、上报埋点这类副作用。
- watchEffect 会立即执行传入函数,并自动追踪同步执行过程中读取到的响应式依赖。依赖变化时重新执行,适合依赖来源较多、但副作用逻辑比较短的场景。
- computed 应该保持纯计算,不要在里面发请求或修改外部状态。watch 和 watchEffect 可以做副作用,但要处理清理逻辑、竞态和触发时机。
- 如果能写成一个派生值,优先用 computed;如果要响应变化后做一件事,用 watch;如果依赖很多又不想手动列来源,可以考虑 watchEffect。
题目解析
这三个 API 的核心区别是“返回值”和“副作用”。computed 本质上是响应式派生值,watch / watchEffect 主要用于观察变化后执行副作用。
watch 的数据源更清楚,适合需要比较新旧值、控制 immediate、deep、flush 的场景。watchEffect 写起来短,但依赖是自动收集的,复杂逻辑里可读性可能下降。
flush 会影响回调执行时机。默认 pre 会在组件 DOM 更新前执行,post 更适合读取更新后的 DOM,sync 会同步触发,使用时要避免频繁更新造成性能问题。
代码示例
这个例子展示 computed 负责派生展示值,watch 负责搜索请求,watchEffect 负责自动跟随依赖输出状态。
Vue
<script setup lang="ts">
import { computed, ref, watch, watchEffect } from 'vue'
const keyword = ref('')
const page = ref(1)
const price = ref(100)
const count = ref(2)
const totalPrice = computed(() => price.value * count.value)
watch(
[keyword, page],
([nextKeyword, nextPage], [oldKeyword]) => {
const requestPage = nextKeyword !== oldKeyword ? 1 : nextPage
if (nextKeyword !== oldKeyword) {
page.value = 1
}
fetchProducts(nextKeyword, requestPage)
},
{ immediate: true }
)
watchEffect(() => {
console.log('当前搜索条件:', keyword.value, page.value)
})
function fetchProducts(keyword: string, page: number) {
console.log('request', { keyword, page })
}
</script>常见误区
- 把 computed 当 watch 用,在 computed 里发请求、改状态或写 localStorage。
- watchEffect 里写太多逻辑,依赖来源变得不清楚,后续很难判断为什么重新执行。
- 需要旧值时使用 watchEffect。watchEffect 不直接提供 oldValue,需要旧值就用 watch。