×

在Vue3的setup中如何使用watch的deep选项?

提问者:Terry2025.04.18浏览:28

在Vue3的开发过程中,`watch` 是一个非常重要的功能,它允许我们监听数据的变化并执行相应的操作,而 `deep` 选项在 `watch` 中更是有着特殊的用途,尤其是当我们需要深度监听对象或数组的变化时,下面我们将详细探讨在Vue3的 `setup` 中如何使用 `watch` 的 `deep` 选项。

什么是watch和deep选项?

在Vue3中,watch 函数用于响应式地监听数据的变化,它接受源(可以是一个响应式数据、一个函数返回值等)和一个回调函数,当源数据发生变化时,回调函数会被执行。

deep 选项是 watch 的一个配置项,它用于告诉Vue是否需要深度监听对象或数组内部的变化,默认情况下,watch 只会监听对象或数组的引用变化,也就是说,如果对象或数组的引用没有改变,即使内部属性或元素发生了变化,watch 也不会触发,而当设置 deep: true 时,Vue会递归地监听对象或数组内部的所有变化。

在setup中如何基本使用watch?

在Vue3的 setup 函数中,使用 watch 非常直观,假设我们有一个简单的响应式数据:

import { ref, watch } from 'vue';
export default {
  setup() {
    const count = ref(0);
    watch(count, (newValue, oldValue) => {
      console.log(`Count has changed from ${oldValue} to ${newValue}`);
    });
    return {
      count
    };
  }
};

在上述代码中,我们使用 ref 创建了一个响应式变量 count,然后使用 watch 监听 count 的变化,每当 count 的值发生改变时,回调函数就会打印出新旧值。

为什么需要deep选项?

考虑以下场景,我们有一个包含多个属性的对象:

import { ref, watch } from 'vue';
export default {
  setup() {
    const user = ref({
      name: 'John',
      age: 30
    });
    watch(user, (newValue, oldValue) => {
      console.log('User has changed');
    });
    // 尝试修改对象内部属性
    setTimeout(() => {
      user.value.age = 31;
    }, 2000);
    return {
      user
    };
  }
};

在这个例子中,我们使用 watch 监听 user 对象,但是当我们在2秒后通过 user.value.age = 31 修改 user 对象的 age 属性时,watch 的回调函数并不会被触发,这是因为 user 对象的引用并没有改变,只是内部属性发生了变化。

这时就需要 deep 选项来解决这个问题。

如何在setup中使用watch的deep选项?

只需要在 watch 的配置对象中设置 deep: true 即可,修改上述代码如下:

import { ref, watch } from 'vue';
export default {
  setup() {
    const user = ref({
      name: 'John',
      age: 30
    });
    watch(user, (newValue, oldValue) => {
      console.log('User has changed');
    }, {
      deep: true
    });
    // 尝试修改对象内部属性
    setTimeout(() => {
      user.value.age = 31;
    }, 2000);
    return {
      user
    };
  }
};

当我们在2秒后修改 user 对象的 age 属性时,watch 的回调函数就会被触发,因为我们开启了深度监听。

深度监听数组的变化

对于数组,同样的道理也适用。

import { ref, watch } from 'vue';
export default {
  setup() {
    const numbers = ref([1, 2, 3]);
    watch(numbers, (newValue, oldValue) => {
      console.log('Numbers array has changed');
    }, {
      deep: true
    });
    // 尝试修改数组元素
    setTimeout(() => {
      numbers.value[1] = 4;
    }, 2000);
    return {
      numbers
    };
  }
};

在这个例子中,我们使用 deep 选项深度监听 numbers 数组,当2秒后我们修改数组的第二个元素时,watch 的回调函数会被触发。

使用watch监听对象或数组的部分属性

我们可能并不想深度监听整个对象或数组,而是只监听其中的部分属性,我们可以通过传递一个函数作为 watch 的第一个参数来实现,我们只想监听 user 对象的 age 属性:

import { ref, watch } from 'vue';
export default {
  setup() {
    const user = ref({
      name: 'John',
      age: 30
    });
    watch(() => user.value.age, (newValue, oldValue) => {
      console.log(`User's age has changed from ${oldValue} to ${newValue}`);
    });
    // 尝试修改age属性
    setTimeout(() => {
      user.value.age = 31;
    }, 2000);
    return {
      user
    };
  }
};

在上述代码中,我们通过 watch(() => user.value.age,...) 只监听 user 对象的 age 属性,这样,当 age 属性发生变化时,回调函数会被触发,而不需要对整个 user 对象进行深度监听。

深度监听的性能问题

虽然 deep 选项非常有用,但深度监听是有性能代价的,因为Vue需要递归地遍历对象或数组的每一层,为每个属性或元素设置响应式,在不必要的情况下,尽量避免使用 deep 选项,如果只是需要监听对象或数组的整体变化,不涉及内部具体属性或元素的变化,就不需要开启深度监听。

如果你只是关心整个 user 对象是否被替换(引用变化),而不是内部属性的变化,那么就不需要使用 deep 选项:

import { ref, watch } from 'vue';
export default {
  setup() {
    const user = ref({
      name: 'John',
      age: 30
    });
    watch(user, (newValue, oldValue) => {
      console.log('User object reference has changed');
    });
    // 替换整个user对象
    setTimeout(() => {
      user.value = {
        name: 'Jane',
        age: 25
      };
    }, 2000);
    return {
      user
    };
  }
};

在这个例子中,当2秒后我们替换整个 user 对象时,watch 的回调函数会被触发,而不需要深度监听。

结合immediate选项使用deep

watch 还有一个 immediate 选项,用于指定回调函数是否在第一次绑定的时候就立即执行,当与 deep 选项结合使用时,需要注意一些细节。

import { ref, watch } from 'vue';
export default {
  setup() {
    const user = ref({
      name: 'John',
      age: 30
    });
    watch(user, (newValue, oldValue) => {
      console.log('User has changed');
    }, {
      deep: true,
      immediate: true
    });
    return {
      user
    };
  }
};

在这个例子中,由于设置了 immediate: true,回调函数会在组件初始化时就立即执行一次,由于 deep: true,后续对象内部的变化也会被监听并触发回调。

需要注意的是,在第一次执行回调时,oldValue 的值是 undefined,因为这是初始状态,还没有旧值,如果需要在初始执行时处理 oldValue,可以进行相应的判断:

import { ref, watch } from 'vue';
export default {
  setup() {
    const user = ref({
      name: 'John',
      age: 30
    });
    watch(user, (newValue, oldValue) => {
      if (oldValue) {
        console.log(`User has changed from ${JSON.stringify(oldValue)} to ${JSON.stringify(newValue)}`);
      } else {
        console.log('Initial value:', JSON.stringify(newValue));
      }
    }, {
      deep: true,
      immediate: true
    });
    return {
      user
    };
  }
};

这样,在初始执行和后续变化时,回调函数都能正确处理新旧值。

在Vue3的 setup 中使用 watchdeep 选项需要根据具体的业务需求来决定,既要确保能够监听到对象或数组内部的变化,又要注意避免不必要的性能开销,通过合理地使用 watchdeep 选项,我们可以更好地处理数据变化带来的业务逻辑。

您的支持是我们创作的动力!

网友回答文明上网理性发言 已有0人参与

发表评论: