Vue中watch和computed的区别

Vue2 中 watch 和 computed 的区别与使用指南

在 Vue2 中,watch 和 computed 都是用于监听数据变化并执行相应操作的 API,但它们在用法和适用场景上存在一些区别。

1. 定义

  • watch: 监听一个特定的数据变化,并在变化时执行回调函数。
  • computed: 根据依赖的数据动态计算出一个新的值,并缓存计算结果,只有当依赖的数据发生变化时才会重新计算。

2. 特点

特性 watch computed
监听方式 监听特定的数据变化 监听依赖的数据变化
返回值 无返回值 返回计算后的值
缓存 无缓存 有缓存
异步 支持异步操作 不支持异步操作
适用场景 数据变化时执行异步操作或复杂逻辑 根据数据动态计算新的值

3. 使用场景

watch 的使用场景

  1. 异步操作
    当需要在数据变化时执行异步操作(例如 API 请求)时,watch 是最佳选择。例如:

    1
    2
    3
    4
    5
    watch: {
    searchQuery(newVal) {
    this.fetchData(newVal); // 当 searchQuery 变化时,触发 API 请求
    }
    }
  2. 复杂逻辑
    当数据变化时需要执行一系列复杂的逻辑操作时,watch 可以更好地组织代码。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    watch: {
    user(newVal) {
    if (newVal.age > 18) {
    this.isAdult = true;
    this.checkEligibility();
    } else {
    this.isAdult = false;
    }
    }
    }
  3. 监听路由变化
    在 Vue Router 中,可以使用 watch 监听路由参数的变化,例如:

    1
    2
    3
    4
    5
    watch: {
    '$route.params.id'(newVal) {
    this.loadUserData(newVal); // 当路由参数 id 变化时,加载用户数据
    }
    }
  4. 深度监听
    当需要监听对象或数组内部的变化时,可以使用 deep 选项:

    1
    2
    3
    4
    5
    6
    7
    8
    watch: {
    user: {
    handler(newVal) {
    console.log('user 对象发生变化');
    },
    deep: true // 深度监听
    }
    }

computed 的使用场景

  1. 动态计算属性
    当需要根据其他数据动态计算出一个新值时,computed 是最佳选择。例如:

    1
    2
    3
    4
    5
    computed: {
    fullName() {
    return this.firstName + ' ' + this.lastName;
    }
    }
  2. 过滤或排序列表
    在需要对列表进行过滤或排序时,computed 可以很方便地实现:

    1
    2
    3
    4
    5
    computed: {
    filteredList() {
    return this.list.filter(item => item.price > 100);
    }
    }
  3. 依赖多个数据
    当计算属性依赖于多个数据时,computed 会自动追踪依赖,并在任一依赖变化时重新计算:

    1
    2
    3
    4
    5
    computed: {
    totalPrice() {
    return this.quantity * this.unitPrice;
    }
    }
  4. 缓存优化性能
    computed 的缓存机制可以避免重复计算,提升性能。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    computed: {
    expensiveCalculation() {
    // 假设这是一个耗时的计算
    let result = 0;
    for (let i = 0; i < 1000000; i++) {
    result += i;
    }
    return result;
    }
    }

4. 注意事项

watch 的注意事项

  1. 避免过度使用
    如果过度使用 watch,可能会导致代码难以维护。对于简单的数据变化,优先考虑使用 computed。

  2. 监听引用类型时的陷阱
    监听对象或数组时,如果直接修改引用类型的属性(例如 this.user.name = 'newName'),watch 可能不会触发。此时需要使用 deep: true 或使用 Vue.set 方法。

  3. 立即执行
    如果需要在监听器创建时立即执行一次回调,可以使用 immediate: true

    1
    2
    3
    4
    5
    6
    7
    8
    watch: {
    user: {
    handler(newVal) {
    console.log('user 发生变化');
    },
    immediate: true
    }
    }

computed 的注意事项

  1. 避免副作用
    computed 应该是纯函数,不应该有副作用(例如修改数据或触发异步操作)。

  2. 依赖追踪
    computed 只会追踪在计算函数中使用的响应式数据。如果依赖的数据没有在函数中使用,computed 不会更新。

  3. 缓存机制
    computed 的缓存是基于依赖的。如果依赖没有变化,即使多次访问 computed 属性,也不会重新计算。


5. 高级用法

watch 的高级用法

  1. 监听多个数据
    可以使用数组监听多个数据:

    1
    2
    3
    4
    5
    watch: {
    ['data1', 'data2'](newVal, oldVal) {
    console.log('data1 或 data2 发生变化');
    }
    }
  2. 监听器卸载
    在组件销毁时,可以手动卸载监听器:

    1
    2
    3
    4
    5
    6
    created() {
    this.unwatch = this.$watch('data', this.handler);
    },
    beforeDestroy() {
    this.unwatch(); // 手动卸载监听器
    }

computed 的高级用法

  1. setter 函数
    computed 可以定义 setter 函数,允许对计算属性赋值:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    computed: {
    fullName: {
    get() {
    return this.firstName + ' ' + this.lastName;
    },
    set(newVal) {
    const names = newVal.split(' ');
    this.firstName = names[0];
    this.lastName = names[1];
    }
    }
    }
  2. 依赖非响应式数据
    如果 computed 依赖的数据不是响应式的,可以使用 Vue.setthis.$set 将其转换为响应式数据。


6. 总结对比表格

特性 watch computed
定义 监听特定数据变化,执行回调函数 根据依赖数据动态计算新值,并缓存结果
返回值
缓存
异步支持 支持 不支持
适用场景 数据变化时执行异步操作或复杂逻辑 根据数据动态计算新的值
性能 无缓存,可能重复执行 有缓存,性能更优
深度监听 支持(需配置 deep: true 不支持
立即执行 支持(需配置 immediate: true 不支持
setter 函数 不支持 支持