Code前端首页关于Code前端联系我们

Vue3里computed和watch该咋选?一篇搞懂使用场景差异

terry 17小时前 阅读数 19 #SEO
文章标签 Vue3;computed watch

做Vue3项目时,很多同学刚开始分不清computed和watch啥时候用,明明都是跟数据变化挂钩,咋选才对?今天从核心差异、场景、执行逻辑这些角度拆明白,以后写代码不纠结~

先搞懂:computed和watch核心差异是啥?

你可以把computed理解成“自动计算的属性”,它依赖其他响应式数据,输出一个“派生结果”;而watch“盯着数据变化后做事的工具”,重点在“数据变了要执行什么动作”。

举个简单对比:

  • computed时,你关心“结果是什么”——比如把 firstName + lastName 拼成 fullName,只要 firstName/lastName 变了,fullName 自动更新;
  • watch时,你关心“变化发生后做什么”——比如用户修改了收货地址,地址变化后要自动调用接口保存到后台,这时候就需要“监听地址变化→执行保存逻辑”。

另外还有个关键区别:computed缓存机制,只要依赖的响应式数据没变化,多次访问计算属性会直接用缓存结果,不会重复计算;但watch没缓存,只要监听的数据变化,就会触发回调函数执行。

这些场景,优先用computed

如果你的需求是“根据已有数据生成新数据”,选computed准没错,这也是它最擅长的事儿~

处理“数据派生”逻辑

比如用户信息里有firstNamelastName,要显示全名fullName,用computed把两个字段拼起来:

const firstName = ref('张')  
const lastName = ref('三')  
const fullName = computed(() => `${firstName.value}${lastName.value}`)  

只要firstNamelastName变了,fullName会自动更新,模板里直接用{{ fullName }}就行,不用每次手动拼接。

简化模板里的复杂逻辑

模板里尽量少写复杂判断或运算(比如多层嵌套的三元表达式、循环计算),否则代码又丑又难维护,把这些逻辑丢进computed,模板只负责渲染结果。

比如购物车判断商品是否全选:

const allChecked = computed(() => {  
  return cartList.every(item => item.checked)  
})  

模板里只用{{ allChecked ? '全选' : '未全选' }},清爽多了~

依赖缓存优化性能

如果一个计算逻辑很耗时(比如遍历大数组做过滤、排序),但依赖的数据不常变化,computed的缓存能帮你省性能。

已完成的任务列表”:

const completedTasks = computed(() => {  
  return taskList.filter(task => task.done)  
})  

只要taskList没新增/修改任务,多次访问completedTasks不会重复执行过滤,直接用上次的结果。

这些场景,必须用watch

watch的优势是“数据变化时执行任意逻辑”,尤其是异步操作、多数据联动这些场景,computed搞不定,必须靠watch

处理异步或副作用操作

computed的回调必须返回一个值,不能写异步代码(比如调接口、定时器),但watch的回调可以随便写异步逻辑。

比如用户修改头像后,要把新头像地址上传服务器:

const avatarUrl = ref('默认头像地址')  
watch(avatarUrl, (newUrl) => {  
  // 调接口上传新地址  
  uploadAvatar(newUrl).then(() => {  
    // 上传成功后做其他事  
  })  
})  

多数据源联动响应

如果要同时监听多个数据的变化,并且在它们变化时执行同一段逻辑,watch更灵活。

比如表单里“用户名”和“密码”都变了,才允许提交按钮可用:

const username = ref('')  
const password = ref('')  
const canSubmit = ref(false)  
watch([username, password], ([newUser, newPwd]) => {  
  canSubmit.value = newUser.length > 0 && newPwd.length > 6  
})  

复杂逻辑的“变化响应”

有些场景逻辑很碎:数据变化后要更新多个状态、调用多个方法、甚至修改其他组件的状态……这种“大动作”适合用watch集中处理。

比如用户切换“暗黑模式”后,要做这些事:

  • 切换页面主题样式;
  • 把选择记录到 localStorage;
  • 通知子组件更新样式;
    watch监听主题变量:
    const isDarkMode = ref(false)  
    watch(isDarkMode, (newMode) => {  
    document.body.className = newMode ? 'dark' : 'light'  
    localStorage.setItem('theme', newMode ? 'dark' : 'light')  
    emit('theme-changed', newMode)  
    })  

监听引用类型的“深层变化”

如果监听的是对象、数组这类引用类型,默认watch只能监听到“引用地址变化”(比如整个对象被重新赋值),如果要监听对象内部属性变化(比如user.info.age变了),得开deep: true选项——这也是computed做不到的。

比如监听用户信息对象的深层变化:

const user = reactive({ info: { age: 18, name: '小明' } })  
watch(  
  () => user.info,  
  (newInfo) => {  
    console.log('用户信息变了', newInfo)  
  },  
  { deep: true } // 开启深层监听  
)  

依赖追踪和执行时机,藏着啥区别?

除了功能场景,computedwatch“什么时候执行”这件事上也不一样,理解这点能避免很多 bug~

computed:惰性求值 + 缓存复用

computed“用的时候才计算”(惰性),比如你定义了一个computed属性,但模板里没用到它,那这个计算逻辑永远不会执行,而且只要依赖的数据没变化,下次用的时候直接拿缓存结果,不会重复计算。

举个🌰:

const count = ref(1)  
const double = computed(() => count.value * 2)  
// 场景1:模板里没用double → computed回调永远不执行  
// 场景2:模板里用了double → 当count从1→2时,double才会重新计算  

watch:主动监听 + 即时执行

watch“数据变了就执行”,不管这个数据有没有被使用,而且每次变化都会触发回调,没有缓存。

比如监听count

watch(count, (newVal) => {  
  console.log('count变了,现在是', newVal)  
})  
// 只要count从1→2→3,每次变化都会触发回调,打印日志  

实际项目咋选?举个栗子更清楚

光说理论太虚,结合真实场景看区别~

案例1:购物车“总价计算”→ 用computed

购物车有goodsList(数组,每个商品有pricequantity),要显示“总价”。

computed的话,依赖goodsList里的每个商品价格和数量,自动计算总和:

const totalPrice = computed(() => {  
  return goodsList.reduce((sum, item) => {  
    return sum + item.price * item.quantity  
  }, 0)  
})  

只要商品的价格或数量变化,总价自动更新,而且重复访问totalPrice会用缓存,性能友好。

案例2:用户登录后“拉取个人信息”→ 用watch

用户登录状态isLoginfalsetrue后,要调用接口拉取个人信息、订单列表等。

这时候用watch监听isLogin的变化,执行异步逻辑:

const isLogin = ref(false)  
watch(isLogin, (newVal) => {  
  if (newVal) { // 登录状态变为true时  
    fetchUserInfo().then((res) => {  
      userInfo.value = res.data  
    })  
    fetchOrderList().then((res) => {  
      orderList.value = res.data  
    })  
  }  
})  

这种“数据变化后执行异步操作”的场景,computed没法搞,因为它不能返回Promise,也不适合写副作用逻辑。

总结一下怎么选

  • 派生新数据(比如拼接、过滤、计算)→ 用computed
  • 数据变化后执行动作(比如异步请求、多状态更新、副作用操作)→ 用watch
  • 性能敏感、依赖少变 → 优先computed(缓存省性能);
  • 要监听引用类型内部变化、多数据联动 → 必须watch

其实记住核心:computed聚焦“结果”,watch聚焦“动作”,下次写代码前,先想清楚你要“得到一个新数据”还是“数据变了要做事”,选择就清晰了~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门