在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
中使用 watch
的 deep
选项需要根据具体的业务需求来决定,既要确保能够监听到对象或数组内部的变化,又要注意避免不必要的性能开销,通过合理地使用 watch
和 deep
选项,我们可以更好地处理数据变化带来的业务逻辑。
网友回答文明上网理性发言 已有0人参与
发表评论: