Skip to content

computed 与 watch 的区别

这个问题是 Vue.js 面试中的高频考点。作为面试官,我希望听到你对这两个核心特性不仅有概念上的理解,更能结合实际场景,清晰地阐述它们的差异和最佳实践。

核心区别概述

computed (计算属性)声明式的,它根据依赖的数据自动计算出一个新的值,并且会缓存计算结果。它更关心的是"结果"。

watch (侦听器)命令式的,它用来观察和响应某个数据的变化,并在变化时执行一个"副作用"(side effect),比如执行异步操作或开销较大的操作。它更关心的是"过程"。

详细区别对比

特性computed (计算属性)watch (侦听器)
功能性质计算、派生。基于一个或多个响应式数据,计算出一个新的值。更像是"是什么"(what)。观察、响应。当某个数据变化时,执行一个动作或副作用。更像是"做什么"(do)。
缓存机制有缓存 (Cachable)。只要它的依赖数据没有发生变化,多次访问计算属性会立即返回之前缓存的计算结果,而不会重新执行函数。无缓存。只要被侦听的数据发生变化,无论新旧值是否相同(默认情况下),都会执行回调函数。
异步支持不支持异步computed 内部必须是同步的 getter 函数,其返回值就是计算结果。在 computed 中进行异步操作是反模式的,也无法得到期望的结果。支持异步 (Asynchronous)watch 的回调函数中可以执行异步操作,例如发送网络请求、设置定时器等。这是它处理副作用的核心优势。
调用方式模板中直接使用,像普通属性一样,不加 ()。Vue 会自动处理依赖追踪。watch 选项中定义,或者使用 this.$watch API 编程式地创建
初始化执行默认不立即执行(懒加载)。只有当第一次访问它时才会计算。默认不立即执行。只有当被侦听的数据第一次改变时才执行。但可以通过 immediate: true 选项让它在组件初始化时立即执行一次。

computed 应用场景

computed 的核心在于简化模板复用逻辑。当一个值依赖于其他几个值,并且这个计算逻辑在模板中多处使用,或者逻辑比较复杂时,就应该使用 computed

场景1:格式化或组合数据

比如,你有一个 firstNamelastName,需要展示完整的 fullName

javascript
data() {
  return {
    firstName: 'John',
    lastName: 'Doe'
  }
},
computed: {
  fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

在模板中直接使用 <p></p> 即可,比 <p> </p> 更清晰,且逻辑可复用。

场景2:购物车总价计算

总价依赖于一个商品列表,每个商品有价格和数量。

javascript
computed: {
  totalPrice() {
    return this.cartItems.reduce((total, item) => total + item.price * item.quantity, 0);
  }
}

只要 cartItems 数组或其中任何一项的 pricequantity 变化,totalPrice 就会自动重新计算。因为有缓存,只要购物车数据不变,无论访问多少次 totalPrice,都只会计算一次。

场景3:根据条件筛选列表

从一个完整的列表中,根据一个搜索词 searchText 动态筛选出需要展示的列表。

javascript
computed: {
  filteredList() {
    return this.originalList.filter(item => item.name.includes(this.searchText));
  }
}

watch 应用场景

watch 的核心在于执行副作用 (side effects)。当一个数据变化,你需要做的不仅仅是计算一个新值,而是要"做一些事情"时,就应该用 watch

场景1:执行异步操作(如网络请求)

当用户在搜索框中输入问题 question 时,你需要去请求 API 获取答案。这个过程是异步的,必须用 watch

javascript
watch: {
  question(newQuestion, oldQuestion) {
    // 当 question 变化时,执行这个函数
    this.answer = 'Thinking...';
    this.fetchAnswer(); // 这是一个异步方法
  }
}

场景2:操作 DOM 或执行开销大的操作

当某个数值 value 变化时,需要驱动一个复杂的动画库或图表库重新渲染。这些操作可能很耗时,不适合放在 computed 里。

javascript
watch: {
  value(newValue) {
    // 调用第三方库来更新图表
    chartLibrary.update(newValue);
  }
}

场景3:侦听复杂数据类型(对象或数组)

当需要侦听一个对象的属性变化时,需要使用 deep: true 选项。

javascript
watch: {
  form: {
    handler(newValue) {
      // 当 form 对象内部的任何属性变化时,执行操作
      // 比如:将表单数据存入 localStorage
      localStorage.setItem('form-data', JSON.stringify(newValue));
    },
    deep: true // 深度侦听
  }
}

选择原则

总的来说,选择用哪个,关键看你的目的:

  • 如果你需要一个依赖其他数据计算而来的新数据,并且希望利用缓存来优化性能,就用 computed
  • 如果你需要在数据变化时执行一些命令式的、有副作用的或者异步的操作,就用 watch

Vue 3 中的变化

在 Vue 3 的 Composition API 中,computedwatch 的核心思想是一致的,只是使用形式变成了 computed()watch() / watchEffect() 函数,这让它们在组合式函数中的使用更加灵活。

javascript
// Vue 3 Composition API
import { computed, watch, ref } from 'vue'

const firstName = ref('John')
const lastName = ref('Doe')

// computed
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

// watch
watch(firstName, (newValue, oldValue) => {
  console.log(`firstName changed from ${oldValue} to ${newValue}`)
})

面试回答要点

回答这个问题时,应该从以下几个方面来组织:

  1. 概念区别:computed 关注结果,watch 关注过程
  2. 核心差异:缓存机制、异步支持、使用方式
  3. 应用场景:举出具体的实例来说明何时使用哪个
  4. 选择原则:能用 computed 就优先用 computed,需要副作用时才用 watch

这样的回答既有理论深度,又有实践场景,逻辑清晰,层次分明,能够充分展示对 Vue 响应式系统的深度理解。

基于 VitePress 构建