Vue3 watch与watchEffect区别
watch 手动(定向)依赖
watch
API 与选项式 APIthis.$watch
(以及相应的watch
选项) 完全等效。watch
需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。
特性
- 惰性地执行;
- 更具体地说明应触发侦听器重新运行的状态;
- 访问被侦听状态的先前值和当前值。
- 可监听单个或多个源数据。
- 默认不会立即执行。添加
{ immediate:true }
可以立即执行,没有惰性。 - 不可以停止监听。
- 添加第三个参数
{deep: true}
深度监听。
示例
import { ref, watch } from 'vue'
export default {
setup() {
// 直接侦听一个
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
}
// 直接侦听多个
const fooRef = ref(0);
const barRef = ref(0);
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
},{deep: true})
return { count, fooRef, barRef }
}
}
watchEffect 自动依赖
立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
特性
watchEffect
不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行。watchEffect
必须立即执行一次(依赖收集);注意: 依赖太多各种坑。- 可停止监听。
示例
import { ref, watchEffect } from 'vue'
export default {
setup() {
const count = ref(0)
setInterval(() => {
count.value++
}, 1000)
/** 这个hook用以监听count.value的变化 */
watchEffect(() => console.log(count.value))
// -> logs 0
// -> logs 1
// -> logs 2
// -> logs 3
// -> logs 4
// -> logs ...
return { count }
}
}
watchEffect 与 watch 有什么不同?
- 第一点我们可以从示例代码中看到
watchEffect
不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行,而watch
只能监听指定的属性而做出变更(v3开始可以同时指定多个)。 - 第二点就是
watch
可以获取到新值与旧值(更新前的值),而watchEffect
是拿不到的。 - 第三点是
watchEffect
如果存在的话,在组件初始化的时候就会执行一次用以收集依赖(与computed
同理),而后收集到的依赖发生变化,这个回调才会再次执行,而watch
不需要,因为他一开始就指定了依赖。
如何选择watch
watchEffect
,根据以上的不同以及相关业务需求做出正确的选择。
watchEffect 进阶
停止监听
watchEffect
会返回一个用于停止这个监听的函数,如法如下:
import { ref, watchEffect } from 'vue'
export default {
setup() {
const stop = watchEffect(() => {
/* ... */
})
// later
stop()
return {}
}
}
如果 watchEffect
是在 setup
或者 生命周期里面注册的话,在组件取消挂载的时候会自动的停止掉。
onInvalidate()
onInvalidate(fn)
传入的回调会在 watchEffect
重新运行或者 watchEffect
停止的时候执行。
watchEffect
的第一个参数——effect
函数——自己也有参数:叫onInvalidate
,也是一个函数,用于清除effect
产生的副作用。(而且onInvalidate
的参数也是函数,哈哈!)
onInvalidate
被调用的时机很微妙:它只作用于异步函数,并且只有在如下两种情况下才会被调用:
- 当 effect 函数被重新调用时
- 当监听器被注销时(如组件被卸载了)
如下代码中,onInvalidate
会在 id 改变时或停止侦听时,取消之前的异步操作(asyncOperation
):
import { asyncOperation } from "./asyncOperation";
const id = ref(0);
watchEffect((onInvalidate) => {
const token = asyncOperation(id.value);
onInvalidate(() => {
// 如果id 已更改或监视程序已停止,则运行
token.cancel();
});
});