vue使用v-model
+ 自定义ref
,实现完美的防抖功能
一、效果实现
二、思路
2.1 传统做法
对于v-model实现的数据双向绑定,无法使用函数进行处理。此时只需要将v-model,拆分为:value和@input,就能实现防抖功能,以下是传统做法:
<template>
<input :value="inputValue" @input="handleInput"/>
<p>{{ inputValue }}</p>
</template>
<script setup>
import { debounced } from './debounced.js'
import { ref } from 'vue'
const inputValue = ref()
const inputFn = (e) => (inputValue.value = e.target.value)
const handleInput = debounced(inputFn, 200)
</script>
// debounced.js
export function debounced (func, delay = 1000) {
let timer
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
func.call(this, ...args)
}, delay)
}
}
2.2 问题分析
1.代码分析
尽管已经将debounced封装成单独的js文件,但只要使用debounced
,每次都需要重复执行下图代码。
有没有办法直接使用v-model,不需要做这么多事情???
2.ref本质
要解决上面的问题,我们只需要了解ref
是如何进行工作的。打印ref
,会发现它的响应式本质就是依赖于Object.defineProperty()
的get()
和set()
,get对数据进行依赖收集(track()
),而set则进行派发更新(trigger()
)。
3.实现方法
既然知道了ref的工作原理,那我们只需要在set()
派发更新时,加个防抖不就行了?因此,我们就需要自定义ref
方法。
打开vue官网,有这么一个api(customRef()
),它会接收一个工厂函数作为参数,这个工厂函数接受 track
和trigger
两个函数作为参数,并返回一个带有get
和set
方法的对象。
通过customRef
完成自定义ref
:
// debouncedRef.js
import { customRef } from 'vue'
export function debouncedRef (value, delay = 500) {
let timer
return customRef((track, trigger) => {
return {
get () {
// 收集依赖
track()
return value
},
set (val) {
clearTimeout(timer)
timer = setTimeout(() => {
// 派发更新
value = val
trigger()
}, delay)
}
}
})
}
到此一个完美的防抖功能就实现了~~~
<template>
<input v-model="inputValue"/>
<p>{{ inputValue }}</p>
</template>
<script setup>
import { debouncedRef } from './debouncedRef.js'
const inputValue = debouncedRef('', 1000)
</script>