watch和watchEffect

386 阅读4分钟

本文只介绍 watchwatchEffect 的用法及注意事项,如果需要更深入了解请阅读源码

watch

侦听一个或多个响应式数据源,并在数据源变化时调用所给的回调函数,默认是懒侦听。

参数

watch 函数接收三个参数

  1. 第一个参数是需要侦听的数据
    • 一个 ref 或者一个响应式对象
    • 一个函数,返回一个值
    • 由以上类型的值组成的数组
  2. 第二个参数是数据发生变化时的回调
    • 回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数
  3. 第三个参数是一个可选的对象
    • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined
    • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调
    • flush:回调函数的刷新时机
    • onTrack / onTrigger:调试侦听器的依赖

副作用清除

开发中需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候停止了侦听器,或者侦听器侦听函数被再次执行了,那么上一次的网络请求应该被取消掉这个时候可以清除上一次的副作用当副作用即将重新执行或者侦听器被停止 时会执行该函数传入的回调函数

const count = ref(0)
watch(count, (val, old, onInvalidate) => {
    const timer = setTimeout(() => {
        console.log('setTimeout')
    }, 2000)
    onInvalidate(() => clearTimeout(timer))
    console.log(val, old)
})
// 先打印val和old,等两秒之后打印setTimeout
// 如果频繁多次触发,这里会多次触发val和old,但只会触发最后一次的setTimeout

停止侦听器

const count = ref(0)
const stop = watch(count, (val, old) => console.log(val, old)
stop() // 当不在需要侦听时调用

flush

  • pre (默认)
    侦听器将在组件渲染之前执行
  • post
    将会使侦听器延迟到组件渲染之后再执行
  • sync
    响应式依赖发生改变时立即触发侦听器

onTrack / onTrigger

onTrack 依赖项被追踪时被调用,只会执行一次。 onTrigger 依赖项变更导致副作用被触发时被调用。只在开发模式下生效

注意

  • 当侦听一个响应式对象时,侦听器会自动启用深层模式
    const state = reactive({ count: 0 })
    watch(state, () => {
      /* 深层级变更状态所触发的回调 */
    })
    
  • watch 只能监听响应式数据:ref 定义的属性和 reactive 定义的对象,如果直接监听 reactive 定义对象中的属性是不允许,只能使用函数转换一下,就是官网说的监听一个 getter
    const state = reactive({count: 0})
    watch(() => state.count, () => {})
    
  • 如果定义了 reactive 的数据,想去使用 watch 监听数据改变,则无法正确获取旧值。如果使用 ref 初始化一个对象或者数组类型的数据,会被自动转成 reactive 的实现方式,生成 proxy 代理对象,也会变得无法正确取旧值

watchEffect

立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行

参数

  1. 第一个参数是要运行的副作用函数。这个函数的参数也是一个函数,可以用来清除无效的副作用
    • 清除副作用函数参考 watch
  2. 第二个参数是一个可选的对象,可以用来调整副作用的刷新时机或者调试副作用的依赖
    • 对象包含flush onTrack onTrigger 。用法参考 watch

停止侦听器

cosnt stop = watchEffect(() => console.log(count.value)
stop()

注意

  • watchEffect 不支持深度监听,如果监听 reactive 定义的对象是不起作用的,只能监听对象中的属性
  • 3.2 版本以上可以用 watchPostEffect watchSyncEffect 代替 flush: 'post' flush: 'sync'
  • watchEffect 动态加入的依赖不会被清除

什么时候应该使用 watch 和 watchEffect

  • 如果需要依赖旧值必须使用 watch
  • 需要深度监听时必须使用 watch
  • watchEffect 侦听数据变化写法比较简单,但是不能轻易的操作依赖值,否则会重复触发
  • 如果回调函数不依赖侦听值那么需要用 watch
  • 需要明确知道由哪个状态触发的则必须用 watch