【vue3.5】onWatcherCleanup 到底咋用?应用场景之一:防抖

833 阅读3分钟

vue3.5推出的新API onWatcherCleanup 要怎么使用?有哪些应用场景?一开始没想出了,后来看了各位大佬写的文章,想到了一种使用方式:防抖!

onWatcherCleanup 基础用法

我们先来试试 onWatcherCleanup 的基础用法,主要看看触发时机。

 const person = reactive({
    count: 0,
    name: 'jyk'
  })

  watch(person, () => {
    // 2、然后才会打印
    console.log('watch -- person', person)
    onWatcherCleanup(() => {
      // 1、先打印
      console.log('onWatcherCleanup -- person', person)
    })
  })

啥时候会打印?先打印谁?

  • person 变化发生后
    • 第一次变化:只触发 watch 的回调
    • 非第一次:先触发 onWatcherCleanup 的回调,后触发 watch 的回调
  • 卸载组件前
    • 如果 person 没有变更,不会触发 onWatcherCleanup 的回调
    • 如果 person 有变更,才会触发 onWatcherCleanup 的回调

小结一下:

  • watch 监听的对象发生变更的时候,onWatcherCleanup 才被执行,回调才会生效;
  • 然后在变更前或者组件卸载前,才会调用 onWatcherCleanup 的回调函数。

列表数据多重 watch 之避免重复加载数据

上次说了列表管理里面,会对查询条件、翻页的页号进行 watch,然后会触发重复申请数据,本来想用 暂停、恢复的方式去解决,但是经过测试发现似乎不行。

现在想想,可以使用 onWatcherCleanup 来解决。

防抖篇

  // 模拟查询条件
  const find = reactive({
    count: 0,
    name: 'jyk'
  })

  // 模拟翻页的页号
  const pager = reactive({
    pageIndex: 1,
    size: 10
  })

  // 列表数据
  const list = shallowRef([])
  
  // 计时器,这个要在外面定义
  let timer: any

  // 加载数据 的方法
  const loadData = async (flag: number) => {
    timer = setTimeout(() => {
      // 加载数据
      console.log('加载数据:loadData')
      // 模拟 加载数据 等待 20ms 被调用
      axios.get('/api/xxx').then(res => {})
    }, 20)

    onWatcherCleanup(() => {
      console.log('onWatcherCleanup 删除 定时器')
      clearTimeout(timer)
    })
  }

  // 监听 查询
  watch(find, async () => {
    console.log('watch -- find', find)
    // 查询条件变更,页号设置为 1 
    pager.pageIndex = 1
    await loadData(find.count * 10)
  }) 
  
  // 监听页号
  watch(() => pager.pageIndex, async () => {
    console.log('watch -- pager.pageIndex', pager)
    await loadData(pager.pageIndex)
  }) 

使用防抖的思路,使用定时器延期发送申请,如果等待时间内有新的申请,那么就取消上次的定时器。

timer 在外面定义,可以支持多个watch,如果在 loadData 内部定义,那么只支持当前的 watch,不支持其他的watch。

这样的话,不管有多少个 watch,在等待时间内,只会申请一次。

和常规防抖的区别

如果你对防抖代码熟悉的话,就会发现,在 loadData 里面直接取消定时器也是一样的。

  let timer: any
  const loadData = async (flag: number) => {
    clearTimeout(timer)
    timer = setTimeout(() => {
      // 加载数据
      console.log('加载数据:loadData')
      // 模拟 加载数据 等待 20ms 被调用
      axios.get('/api/xxx').then(res => {})
    }, 20)
  }

这个确实。如果硬要找区别的话,那么就是当组件卸载的时候也会取消定时器。

取消申请篇

axios也支持取消申请,那么如果要取消的话,如何实现呢?

其他代码都一样,改一下 loadData 即可:

  const controller = new AbortController()
  const loadData2 = async (flag: number) => {
    onWatcherCleanup(() => {
      // 取消申请
      console.log('onWatcherCleanup 取消申请')
      controller.abort()
    })
    // 加载数据
    axios.get('/foo/bar', {
      signal: controller.signal
    }).then((response: any) => {
      //...
     
    })
  }

防抖还是取消?

我个人倾向于防抖,毕竟能不折腾后端就不折腾后端。当然了,见仁见智。

参考资料

这应该是全网最详细的Vue3.5版本解读

牛逼!在Vue3.5中仅仅2分钟就能封装一个自动cancel的fetch函数

Vue3.5优化watch监听,使watch更加全面

axios请求取消、请求竞态、请求重试