Vue2极简教程010:computed vs watch 深度对比指南

一、核心概念对比

特性computedwatch
本质计算属性监听器
触发时机依赖数据变化时自动计算指定数据变化时执行回调
返回值必须返回结果无返回值
异步支持不支持支持
缓存机制有缓存(依赖不变不重新计算)无缓存(每次变化都执行)
适用场景模板中使用的派生数据数据变化时需要执行副作用操作

二、语法结构对比

1. computed 基础用法

computed: {
  // 简写形式(仅getter)
  fullName() {
    return this.firstName + ' ' + this.lastName
  },
  
  // 完整形式(getter/setter)
  reversedMessage: {
    get() {
      return this.message.split('').reverse().join('')
    },
    set(newVal) {
      this.message = newVal.split('').reverse().join('')
    }
  }
}

2. watch 基础用法

watch: {
  // 基本监听
  firstName(newVal, oldVal) {
    console.log(`名字从 ${oldVal} 变为 ${newVal}`)
  },
  
  // 深度监听对象
  user: {
    handler(newVal) {
      console.log('用户信息变化', newVal)
    },
    deep: true,  // 深度监听
    immediate: true  // 立即执行
  },
  
  // 监听特定属性
  'obj.property'(newVal) {
    // 处理变化
  }
}

三、工作原理解析

computed 实现机制

graph LR
    A[响应式依赖] --> B(computed计算)
    B --> C{依赖是否变化?}
    C -->|否| D[返回缓存值]
    C -->|是| E[重新计算并缓存]

watch 实现机制

graph LR
    A[监听目标] --> B{值是否变化?}
    B -->|是| C[执行回调函数]
    B -->|否| D[不执行任何操作]

四、经典场景对比

适用 computed 的场景

  1. 表单验证状态

    computed: {
      isFormValid() {
     return this.username.length > 0 && 
            this.password.length >= 6 &&
            this.agreeTerms
      }
    }
  2. 过滤列表数据

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

适用 watch 的场景

  1. 异步数据获取

    watch: {
      searchText(newVal) {
     clearTimeout(this.timer)
     this.timer = setTimeout(() => {
       this.fetchResults(newVal)
     }, 500)
      }
    }
  2. 路由参数变化

    watch: {
      '$route.params.id'(newId) {
     this.loadUserData(newId)
      }
    }

五、性能优化建议

  1. computed 最佳实践

    • 避免在计算属性中做复杂计算(超过1ms应考虑缓存)
    • 不要修改依赖数据(应使用setter)

      // 反模式
      computed: {
      badExample() {
        this.someData = process(this.otherData) // 错误!
        return this.someData
      }
      }
  2. watch 最佳实践

    • 对集合类型数据使用deep: true要谨慎
    • 大量数据监听时使用immediate: false

      watch: {
      bigData: {
        handler() { /* 处理 */ },
        deep: true,
        immediate: false // 避免初始化执行
      }
      }

六、特殊用法技巧

1. computed 的依赖追踪

// 只会追踪this.a和this.b的变化
computed: {
  example() {
    return this.a + this.b + window.externalVar // 不追踪window.externalVar
  }
}

2. watch 的强制触发

this.$watch(
  'someData',
  () => { /* 处理 */ },
  { flush: 'post' } // DOM更新后触发
)

七、企业级应用示例

购物车总价计算(computed)

computed: {
  totalPrice() {
    return this.cartItems.reduce((sum, item) => {
      return sum + (item.price * item.quantity)
    }, 0)
  },
  discountPrice() {
    return this.totalPrice * (1 - this.discountRate)
  }
}

用户权限变更处理(watch)

watch: {
  userRole(newRole) {
    this.resetUI()
    this.fetchMenu(newRole)
    this.checkLicense(newRole)
    
    // 路由跳转
    if (newRole === 'guest') {
      this.$router.push('/login')
    }
  }
}

八、选择决策流程图

graph TD
    A[需要响应数据变化?] --> B{需要新值吗?}
    B -->|是| C{是否依赖多个数据源?}
    C -->|是| D[使用 computed]
    C -->|否| E{需要异步操作吗?}
    E -->|是| F[使用 watch]
    E -->|否| D
    B -->|否| G{需要执行副作用吗?}
    G -->|是| F
    G -->|否| H[可能都不需要]

黄金法则

  • 当需要基于现有数据计算新数据时 → computed
  • 当需要在数据变化时执行操作时 → watch
  • 涉及异步操作或昂贵操作时 → watch + 防抖
  • 模板中使用的派生数据 → 必须用computed

添加新评论