watch 、watchEffect 、computed

bug: 使用watchEffect 时,方法被多次调用

watch vs watchEffect 的区别

特性watchwatchEffect
使用方式监听特定的响应式数据变化(ref、reactive 的属性)自动收集依赖,响应依赖项变化时自动重新执行回调
精确性更加精确,只监听指定的响应式源响应式依赖自动收集,容易造成过度监听
参数传入(newValue, oldValue)没有直接参数
控制能力可以通过 immediatedeepflush 等选项精准控制缺乏对旧值、新值、深度监听等精确控制
推荐场景精准监听某些响应式变量的变化,如表单变化、状态变化等快速原型开发、调试中用于临时打印数据
副作用管理推荐用于执行副作用(如请求、日志、表单校验)不推荐用于副作用逻辑


watch 的典型用法与参数说明

import { ref, watch } from 'vue'
​
const count = ref(0)
​
watch(
  count,
  (newVal, oldVal) => {
    console.log('new:', newVal, 'old:', oldVal)
  },
  {
    immediate: true, // 立即执行一次
    deep: false,     // 是否深度监听(对对象或数组等嵌套结构有效)
    flush: 'post',   // 回调触发的时机('pre'、'post'、'sync')
  }
)

参数说明

  • immediate: 默认是 false,如果为 true,会在监听开始时立即执行一次回调。

    immediate: true

    用于需要初始执行一次逻辑的场景(例如初始加载数据)。

  • deep: 默认是 false。当监听的是一个对象或数组时,设置为 true 可以深度监听内部变化。

    deep: true
  • flush: 控制回调执行的时机:

    • 'pre': DOM 更新前(默认)

    • 'post': DOM 更新后

    • 'sync': 同步执行


oldValuenewValue 的用法

watch 的回调中会提供两个参数:

watch(
  someRef,
  (newVal, oldVal) => {
    console.log('值变化了:', oldVal, '->', newVal)
  }
)

常用于:

  • 判断前后值是否有本质变化(避免重复请求)

  • 回滚操作(如表单数据被错误修改)

  • 依赖旧值进行更新逻辑(如分页切换时保存上一个页码)


 为什么不推荐使用 watchEffect 进行副作用操作?

import { watchEffect } from 'vue'
​
watchEffect(() => {
  console.log('当前值为:', someRef.value)
  fetchData(someRef.value) // 不推荐这样写副作用!
})

原因如下:

  1. 依赖不明确 watchEffect 自动收集依赖,开发者难以控制依赖边界,可能导致非预期的多次执行。

  2. 副作用频繁触发 所有依赖一旦变化就会重新执行,即使变化无关紧要,也会重复调用副作用逻辑(如网络请求)。

  3. 无法访问旧值 相较于 watchwatchEffect 无法拿到 oldValue,难以做“增量式更新”或“比较前后”的逻辑判断。

  4. 难以调试和维护 自动收集依赖导致逻辑混乱,后期维护困难,尤其是在复杂业务场景中。

适合场景(推荐):

  • 快速原型调试

  • 简单的计算、打印逻辑

  • 响应式日志或测试环境下的变量追踪


总结

项目watchwatchEffect
依赖收集手动指定自动收集
是否能获取旧值 oldValue 支持 不支持
是否适合副作用 适合精确副作用逻辑 不推荐用于副作用
精细控制 immediate、deep、flush 等控制 缺乏控制
推荐场景表单监听、API 请求、状态同步等简单数据跟踪、调试打印等

computedwatch 的区别一览

特性computed 计算属性watch 侦听器
作用根据依赖自动计算并返回一个值监听数据变化,执行副作用(如异步操作)
返回值返回一个值不返回值,只执行回调函数
缓存 有缓存,依赖不变则不重新计算无缓存,依赖变就执行
用于展示 适合用于模板或 UI 展示 不适合直接用于展示
是否适合副作用 不适合 专门用于副作用操作(如 API 请求)
能否访问旧值 无 可以拿到 oldValue 和 newValue


computed 的典型使用

import { ref, computed } from 'vue'
​
const firstName = ref('Tao')
const lastName = ref('Yao')
​
// 计算全名
const fullName = computed(() => {
  return `${firstName.value} ${lastName.value}`
})
​
// 也可以有 setter(双向绑定)
const reversed = computed({
  get: () => fullName.value.split('').reverse().join(''),
  set: (val) => {
    // 通常不推荐这么用,仅演示写法
    fullName.value = val.split('').reverse().join('')
  }
})

特点:

  • 响应式:依赖的响应式数据改变时,自动更新。

  • 有缓存:如果依赖没变,重复访问不会重新计算。

  • 常用于模板展示、计算逻辑


使用建议总结

场景推荐使用方式说明
模板展示、纯值计算computed例如:金额格式化、过滤已完成任务、拼接姓名等
数据变化后需执行副作用(请求等)watch如:用户输入搜索词,请求后台接口
需要访问旧值以做比较处理watch如:当值变化时记录日志,或者判断是否真正发生变化
依赖多个数据源组合出一个新值computed计算属性能自动管理多个依赖的组合逻辑
想要延迟执行逻辑(防抖、节流)watch + lodash.debouncecomputed 无法处理副作用延迟逻辑
想要响应式地观察整个对象watch(obj, cb, { deep: true })computed 无法观察对象内部变化


错误使用示例提醒

// ❌ computed 中做副作用(不推荐!)
const wrong = computed(() => {
  fetch('/api/data') // 不应在 computed 中做请求
  return 123
})

副作用请始终用 watchcomputed 只用于值的“推导”。

 小结一句话口诀

computed 管值、watch 管事,展示选 computed,操作选 watch。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值