likes
comments
collection
share

深入理解Vue3中的watch与watchEffect的使用与区别

作者站长头像
站长
· 阅读数 5

导图大纲

深入理解Vue3中的watch与watchEffect的使用与区别

watch

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

watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。

watch 有三个参数

第一个参数:

第一个参数是侦听器的源

  • 一个函数,返回一个值
  • 一个 ref
  • 一个响应式对象
  • ...或是由以上类型的值组成的数组

第二个参数

第二个参数是在发生变化时要调用的回调函数。 这个回调函数接受三个参数:新值、旧值,以及一个用于注册副作用清理的回调函数。 该回调函数会在副作用下一次重新执行前调用,可以用来清除无效的副作用,例如等待中的异步请求。

当侦听多个来源时,回调函数接受两个数组,分别对应来源数组中的新值和旧值。

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
  /* ... */
})

第三个参数

第三个可选的参数是一个对象;

  • immediate:在侦听器创建时立即触发回调。第一次调用时旧值是 undefined。
  • deep:如果源是对象,强制深度遍历,以便在深层级变更时触发回调。参考深层侦听器
  • flush:调整回调函数的刷新时机。参考回调的刷新时机watchEffect()
  • onTrack / onTrigger:调试侦听器的依赖。参考调试侦听器

注意

当直接侦听一个响应式对象时,侦听器会自动启用深层模式:

const state = reactive({ count: 0 })
watch(state, () => {
  /* 深层级变更状态所触发的回调 */
})

侦听一个 getter 函数:

当 侦听 一个 getter 函数时,回调只在此函数的返回值变化时才会触发,。 如果想让深层对象变化时,也被侦听调用,可以加 { deep: true } 强制侦听器进入深层级模式

const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  },
  {
    deep: true
  }
)

侦听 一个 ref

const count = ref(0)
watch(count, (count, prevCount) => {
  /* ... */
})

watchEffect()

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

watchEffect 接受两个参数

第一个参数

第一个参数是数据发生变化时执行的回调函数

  • 当监听的值发生变化时,会自动再次执行以下回调函数
watchEffect(() => {
    //监听objData.str
    console.log(objData.str)
    // 会在 props 变化时打印
    console.log(name, phone, age)
    //海军 1234567 22
  })

第二个参数

第二个参数是一个可选的对象,支持 flush 和 onTrack / onTrigger 选项,功能和 watch 相同。

watchEffect(() => {}, {
  flush: 'post',
  onTrack(e) {
    debugger
  },
  onTrigger(e) {
    debugger
  }
})

停止监听

const stop = watchEffect(() => {})

// 当不再需要此侦听器时:
stop()

注意

注意:watchEffect 仅会在其同步执行期间,才追踪依赖。使用异步回调时,只有在第一个 await 之前访问到的依赖才会被追踪。

watch vs watchEffect

两者区别

watch 和 watchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

  • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机

  • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确

访问 Vue 更新之后的 DOM

在 Vue2.x 中, 使用 nextTick, 在Vue3 中,watch / watchEffect 指明 flush: 'post' 选项 即可。

watch(source, callback, {
  flush: 'post'
})

watchEffect(callback, {
  flush: 'post'
})

watchEffect 有个别名 也可以 后置刷新 watchPostEffect();

import { watchPostEffect } from 'vue'

watchPostEffect(() => {
  /* 在 Vue 更新后执行 */
})

小技巧

关闭监听器

手动停止一个侦听器,请调用 watch 或 watchEffect 返回的函数:

const stopWatchEffect = watchEffect(() => {
})


stopWatchEffect()



const stopWatch = watch(() => {
})


stopWatch()

异步数据加载

如果需要等待一些异步数据,你可以使用条件式的侦听逻辑:

// 需要异步请求得到的数据
const data = ref(null)

watchEffect(() => {
  if (data.value) {
    // 数据加载后执行某些操作...
  }
})