computed用于派生缓存值,watch用于执行副作用;前者适合多次读取的同步计算,后者适合发请求、改DOM等异步或开销大操作,二者不可互换。

直接说结论:computed 用于派生值,自动缓存、响应依赖;watch 用于执行副作用,比如发请求、改 DOM、调 API。两者不能互相替代,选错会导致性能浪费或逻辑失效。
computed 适合什么场景
当你需要基于 data 或其他响应式变量「算出一个新值」,且这个值会被多次读取(比如模板里用了 3 次 {{fullName}}),就该用 computed。
它内部有缓存机制:只要依赖没变,多次访问 fullName.value 不会重复执行函数;而 methods 每次渲染都调用,容易重复计算。
- 不能在
computed函数里修改响应式数据(比如this.count++),否则报错 - 不支持异步,返回值必须是同步结果(
async函数 +await会返回Promise,模板里显示[object Promise]) - 在
setup语法糖中,要用computed(() => ...),不是对象写法
const firstName = ref('Kobe')
const lastName = ref('Bryant')
const fullName = computed(() => firstName.value + ' ' + lastName.value)
// 模板中 {{ fullName }} 会自动更新,且只在 firstName/lastName 变时重算
watch 怎么监听单个值、对象、数组
watch 的核心是「侦听变化并做事情」,不是为了返回值。它默认不触发初始值,要加 immediate: true 才会在绑定时执行一次。
- 监听基本类型(如
count):直接写watch(count, (newVal, oldVal) => {...}) - 监听响应式对象(如
user):需加deep: true才能捕获内部属性变化,但注意性能开销 - 监听数组:默认只监听引用变化(push/pop 改变长度会触发,但
arr[0] = 'x'不会,得用deep: true或监听整个数组的.length) - 监听多个来源?用数组语法:
watch([count, name], ([newC, newName], [oldC, oldN]) => {...})
const user = reactive({ name: 'Alice', age: 25 })
watch(user, (newVal, oldVal) => {
console.log('user changed:', newVal)
}, { deep: true, immediate: true })
为什么不能用 watch 替代 computed 做派生计算
因为 watch 没有返回值,也不参与响应式依赖追踪。你无法在模板里直接写 {{ userInfo }} 并让它自动更新——除非你手动把结果存进另一个 ref 或 data 字段,但这绕远了,还容易漏更新。
- 比如想把
firstName和lastName拼成fullName,用watch就得多写一个fullNameRef = ref(''),再在 handler 里赋值,冗余且易错 -
computed是声明式的:你只说「我要这个值」,框架负责什么时候算、要不要缓存;watch是命令式的:你得自己管「什么时候触发」「怎么存结果」「要不要防抖」 - 深层嵌套对象的依赖追踪,
computed自动处理,watch需手动deep: true,且无法精确到某个字段(比如只想监听user.profile.avatar,就得用字符串路径或 getter 函数)
容易被忽略的边界点
在 uni-app 中,watch 的 immediate 和 deep 经常一起用,但要注意:如果监听的是从接口返回的嵌套对象(比如 res.data.user),而该对象本身是普通 JS 对象(非 reactive),deep: true 无效——必须先用 reactive() 或 ref() 包一层,否则监听不到任何变化。
另外,computed 在 onLoad 钩子中可能还没初始化完成,如果此时直接读取其值(比如用于 API 参数),要加空值判断或用 nextTick 等待。











