结论先行:
watch 和 watchEffect 都是监听器,都是用来监听响应式数据的变化并执行相应操作。区别是:
watch:需要指明要监听的数据,而且在回调函数中可以获取到属性变化的前后值;
适用于需要精确控制监视范围的情况;也就是需要针对特定数据变化执行操作。
watchEffect:不用指明监听哪个属性,回调中用到哪个响应式数据,那就监听哪个。
适用于不需要明确定义监视的数据,只需在回调函数内部使用响应式数据并执行相应操作的场景。也就是只需根据数据变化自动追踪的操作。
watchEffect 有点像计算属性 computed:但计算属性必须要写返回值,而 watchEffect 更注重的是过程(回调函数的函数体),所以不用写返回值。
而且,computed 若是值没有被使用时不会调用,但是 watchEffect 始终会调用一次。
具体解析:
watch 和 watchEffect 都是监听器(侦听器)。用来监听响应式数据的变化并执行相应操作
1、watch
- 用于对特定的响应式数据进行监视,并在数据变化时执行相应的操作。
- 需要显式地指定要监视的数据,并提供回调函数来响应数据变化。
- 除了监视简单的数据变化外,还可以处理更复杂的监视需求,如监听嵌套对象或数组的变化,并执行相应的操作。
- 具有一定的惰性。第一次页面展示的时候不会执行,只有数据变化的时候才会执行(设置immediate: true 时可以变为非惰性,页面首次加载就会执行)
它接受3个参数:
- 一个响应式引用 ref 或一个返回值的 getter 函数
- 一个回调
- 可选的配置选项
① watch 侦听单个数据源
import { ref, watch } from 'vue'
const counter = ref(0)
watch(counter, (newValue, oldValue) => {
console.log('The new counter value is: ' + counter.value)
})
应用到实际例子中:
// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs } from 'vue'
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
// 在我们组件中
setup (props) {
// 使用 `toRefs` 创建对prop的 `user` property 的响应式引用
const { user } = toRefs(props)
const repositories = ref([])
const getUserRepositories = async () => {
// 更新 `prop.user` 到 `user.value` 访问引用值
repositories.value = await fetchUserRepositories(user.value)
}
onMounted(getUserRepositories)
// 在用户 prop 的响应式引用上设置一个侦听器
watch(user, getUserRepositories)
return {
repositories,
getUserRepositories
}
}
},
在我们的 setup
的顶部使用了 toRefs
。这是为了确保我们的侦听器能够对 user
prop 所做的更改做出反应。
② watch 侦听多个数据源
第一个参数以数组形式传入,第二个参数回调返回的结果也是数组
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
Vue3 中监听 reactive 中的值,必须以 getter 函数 的形式,不然会报错。和 Vue2 的区别是不用写 deep 属性,默认就是深度监听了。
watch([result2, () => data.title], (newV, oldV) => {
console.log(newV, oldV) // [20, "111"] [20, "222"]
})
监听 reactive 中的多个值时:
watch([result2, () => [data.title, data.value1]], (newV, oldV) => {
console.log(newV, oldV)
})
2、watchEffect
立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。
- 用于创建一个自动追踪其依赖,并在依赖变化时自动运行的响应式副作用。
- 不需要显式地指定要监视的数据,而是根据回调函数内部使用到的响应式数据自动建立依赖关系。
- 适用于无需明确定义监视的数据,只需在回调函数内部使用响应式数据并执行相应操作的场景。
立即执行,没有惰性,页面的首次加载就会执行
无法获取到原值,只能得到变化后的值
<template>
<div>
<h1>watchEffect - 侦听器</h1>
<p>{{data.count}}</p>
<button @click="stop">手动关闭侦听器</button>
</div>
</template>
<script>
import { reactive, watchEffect } from "vue";
export default {
name: "WatchEffect",
setup() {
const data = reactive({ count: 1 });
const stop = watchEffect(() => console.log(`侦听器:${data.count}`));
setInterval(() => {
data.count++;
}, 1000);
return { data, stop };
}
};
</script>
结果:
3、总结
watch:既要指明要监听的属性,也要指明回调。
watchEffect:不用指明监听哪个属性,回调中用到哪个属性,那就监听哪个属性。
watchEffect 有点像计算属性 computed:
但 computed 注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
而 watchEffect 更注重的是过程(回调函数的函数体),所以不用写返回值。
与 watchEffect 比较,
watch
允许我们:
- 懒执行副作用;
- 更具体地说明什么状态应该触发侦听器重新运行;
- 访问侦听状态变化前后的值。