我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。
防抖和节流是我们常遇到的场景,例如使用防抖处理连续点击、使用节流处理滚动条滚动事件。那么问题来了,你有没有遇到需要对watch的回调函数进行控制的场景呢?如果有,那么推荐你学习一下watchWithFilter。
watchWithFilter 是一个有事件过滤控制器(EventFilter)的watch。和watch类似,但是提供了额外的事件过滤器(eventFilter),并将之应用到watch的回调函数中。本文分析了watchWithFilter的使用方法以及源码实现,欢迎阅读了解。
1. 示例
在本小节,我们首先通过官方示例demo来了解watchWithFilter的使用,接下来还会演示2个为了完成同样功能不用watchWithFilter而自己手动实现的例子。通过对比示例代码理解watchWithFilter的优势。
1.1 官网示例
结合官方文档给出的示例写了一个demo, 代码如下:
<script setup lang="ts">
import { debounceFilter, watchWithFilter } from '@vueuse/core'
import {ref} from 'vue'
const source = ref(0)
watchWithFilter(
source,
() => { console.log('changed!') }, // callback will be called in 500ms debounced manner
{
eventFilter: debounceFilter(500), // throttledFilter, pausabledFilter or custom filters
},
)
const clickedFn = () => {
source.value++
}
</script>
<template>
<div>{{source}}</div>
<button @click="clickedFn">
点击按钮
</button>
</template>
如上代码所示,引入了watchWithFilter和debounceFilter,使用debounceFilter对回调进行防抖,延时事件为500毫秒。代码运行效果如下:
点击了6次按钮但是只触发了2次watch回调。下面看一下使用watch结合手写的防抖函数处理过的函数的写法:
1.2 手动控制方法1
<script setup lang="ts">
import { createFilterWrapper, debounceFilter } from '@vueuse/core'
import {ref, watch} from 'vue'
import type { Ref } from 'vue'
type MaybeRef<T> = T | Ref<T>
type FunctionArgs<Args extends any[] = any[], Return = void> = (...args: Args) => Return
const source = ref(0)
const callback = () => console.log('changed!')
// 手写一个防抖
const debouncedFn = (ms:MaybeRef<number>, fn:FunctionArgs) => {
return createFilterWrapper(debounceFilter(ms), fn)
}
const watchCallback = debouncedFn(500, callback)
watch(
source,
() => {
watchCallback()
},
)
const clickedFn = () => {
source.value++
}
</script>
<template>
<div>{{source}}</div>
<button @click="clickedFn">
点击按钮
</button>
</template>
这里引入了createFilterWrapper, debounceFilter并使用它们写了一个防抖函数debouncedFn,在watch的回调中调用由debouncedFn返回的防抖回调函数watchCallback。代码运行效果如下:
点击了3次按钮但是只触发了1次watch回调。当然也可以使用useDebounceFn使得上面的代码更加简化:
1.3 手动控制方法2
<script setup lang="ts">
import { useDebounceFn } from '@vueuse/core'
import {ref, watch} from 'vue'
const source = ref(0)
const callback = () => console.log('changed!')
const watchCallback = useDebounceFn(callback, 500)
watch(
source,
() => {
watchCallback()
},
)
const clickedFn = () => {
source.value++
}
</script>
<template>
<div>{{source}}</div>
<button @click="clickedFn">
点击按钮
</button>
</template>
如上代码引入了useDebounceFn,并用其对回调函数callback进行封装,watch执行的回调是防抖后的函数watchCallback。代码执行效果如下:
点击了3次按钮但是只触发了1次watch回调。
感觉这个版本的代码不比使用watchWithFilter复杂,但是感觉没有那种方式优雅,有同感吗?感觉那种通过参数配置的方式来写代码看起来高大上,下面就来分析一下watchWithFilter的源码。
2.源码分析
import { watch } from 'vue-demi'
import { bypassFilter, createFilterWrapper } from '../utils'
export function watchWithFilter<Immediate extends Readonly<boolean> = false>(
source: any,
cb: any,
options: WatchWithFilterOptions<Immediate> = {},
): WatchStopHandle {
const {
eventFilter = bypassFilter,
...watchOptions
} = options
return watch(
source,
createFilterWrapper(
eventFilter,
cb,
),
watchOptions,
)
}
watchWithFilter方法体中首先对参数options进行解构,获取eventFilter和watchOptions(传递给watch的选项)。eventFilter就是要用来对回调函数cb进行包装的函数,这里配合createFilterWrapper来使用,关于createFilterWrapper我们已经在之前的文章中分析了。eventFilter的默认值是bypassFilter,其源码如下:
export const bypassFilter: EventFilter = (invoke) => {
return invoke()
}
代码非常简单,接收参数invoke, 并返回对invoke的调用。至此整个watchWithFilter的源码分析完了,是不是非常简单呢?
3.总结
watchWithFilter用于对watch的回调函数进行控制,本质上在于传给watch的回调函数是使用createFilterWrapper函数以及相应的过滤器函数eventFilter包装后的。本文的demo代码已经上传至github,欢迎clone代码并亲自尝试一下watchWithFilter的使用~
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。