之前的文章详细的介绍了vue3.0 相应式原理,知道了用proxy代替Object.defineProperty 的利与弊,了解了依赖收集和派发更新的大致流程,知道了vue3.0响应式原理,这节我们一起研究vue3.0中的 watch 有那些变化。
一 watch 和 watchEffect
之前我们讲解到,vue3.0取消了渲染watch概念,取而代之的effect副作用钩子,来完成当依赖项更改而促使视图。
/* 创建一个渲染 effect */
instance.update = effect(function componentEffect() {
//...省去的内容后面会讲到
},{
scheduler: queueJob })
接下来我们一起分析 watch 和 watchEffect
watch 和 watchEffect 使用
watchEffect
export function watchEffect(
effect: WatchEffect,
options?: BaseWatchOptions
): StopHandle {
return doWatch(effect, null, options)
}
从watchEffect参数有两个,第一个是副作用函数effect,第二个是参数配置项 options ,我们接下来一一解析各参数的用法。
①依赖项监听
import {
reactive, watchEffect } from 'vue'
const state = reactive({
count: 0
})
watchEffect(() => {
const number = `my age is ${
state.count}`
console.log(number)
})
watchEffect需要一个应用所需副作用的函数fn。它立即执行函数,并跟踪在执行过程中作为依赖项使用的所有反应状态属性。在这里state中引入的状态将在初始执行后作为此观察程序的依赖项进行跟踪。什么时候状态在将来的某个时间发生改变时,内部函数将再次执行。
我们可以得出结论
1 首先这个watchEffect函数立即执行一次。
2 里面用到的reactive产生的state里面的count会被作为依赖项跟踪,当触发set,依赖项改变,函数再次执行,达到监听的目的。
②清除副作用
当我们在watchEffect 副作用函数中做一些,dom监听或者定时器延时器等操作的时候,组件卸载的时候需要及时清除这些副作用,避免带来一下滞后的影响,我们需要一个好比在react中useEffect钩子的clean清除函数的功能,同样vue3.0也提供了类似的方法。
watchEffect((onInvalidate)=>{
const handerClick = ()=>{
}
document.addEventListener('click',handerClick)
onInvalidate(()=>{
/*
执行时机: 在副作用即将重新执行时,如果在setup()或生命周期钩子函数中使用watchEffect, 则在卸载组件时执行此函数。
*/
document.removeEventListener('click',handerClick)
})
})
③停止监听
vue3.0 对于2.0的watch也做了功能上的弥补,我们可以在必要的时候手动操作终止这些监听效果。
自动停止监听:当watchEffect在组件的setup()函数或生命周期钩子被调用时,侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。
手动停止监听:
const watcherStop=watchEffect(()=>{
})
watcherStop()
③异步操作
vue3.0 中watchEffect 并不像 react中 useEffect那样不支持异步 async await 预发糖,对异步操作完全支持。
watchEffect(async () => {
})
对于watchEffect第二个参数,主要对watchEffect提供独立的配置项。对监听效果作出调试作用。
export interface BaseWatchOptions {
flush?: 'pre' | 'post' | 'sync'
onTrack?: ReactiveEffectOptions['onTrack']
onTrigger?: ReactiveEffectOptions['onTrigger']
}
flush
从源码中我们可以看出,options 配置参数有三个分别是flush,onTrack和onTrigger
在需要同步或在组件更新之前重新运行watcher效果的情况下,可以使用flush选项传递一个附加的options对象(默认值为“post”)
watchEffect(
() => {
},
{
flush: 'sync' // 同步触发
}
)
watchEffect(
() => {
},
{
flush: 'pre' // 在组件更新之前触发
}
)
onTrack和onTrigger
watchEffect(
() => {
},
{
onTrigger(e) {
//当依赖项的变化触发watcher回调时,将调用onTrigger
console.log('依赖项改变,触发set')
},
onTrack(e){
//
console.log('依赖项被调用,触发get)
}
}
)
如上我们可以得知:
onTrack 当依赖项的变化触发watcher回调时,将调用onTrigger
onTrigger 当state性属性或ref作为依赖项被调用时候,将调用onTrack。
讲完了watchEffect的基本用法,接下来我们看看watch的用法。
watch
watchapi完全等同于2.x this.$watch(以及相应的watch options)。监视需要监视特定的数据源,并在单独的回调函数中应用副作用。默认情况下,它也是惰性的,即只有当被监视的源发生变化时才调用回调。
与watchEffect相比,watch允许我们:
1 懒散地执行副作用
2 更具体地说明什么状态应该触发观察者重新运行;
3 访问被监视状态的先前值和当前值。
// 监听state
const state = reactive({
count: