<script setup>
import { debounceRef } from './ref';
const text = debounceRef('',1000)
</script>
<template>
<div>
<input v-model="text">
</div>
<div>{{text}}</div>
</template>
<style scoped>
</style>
我们想实现这样的效果:输入框中双向绑定了一个响应式数据text,当我们从输入框中改变text中的值时,我们想要text的值过一定的时间再做变更,类似一个防抖效果。
所以上面的关键就是实现debounceRef()这个函数
debounceRef(value,delay)
import { customRef } from "vue";
export function debounceRef(value,delay = 1000){
let timer;
return customRef((track,trigger) =>({
get(){
//依赖收集
track()
return value
},
set(val){
clearTimeout(timer)
timer = setTimeout(()=>{
//派发更新
trigger()
value = val
},delay)
}
}))
}
函数接收两个参数:
第一个参数,绑定的值。
第二个参数,想要延迟多少秒更新
我们利用了vue内部提供的一个实现自定义ref的函数,该函数接收一个工厂函数,该函数必须返回一个对象,其中包含get和set函数
customRef()源码
export function customRef<T>(factory: CustomRefFactory<T>): Ref<T> {
return new CustomRefImpl(factory) as any
}
export type CustomRefFactory<T> = (
track: () => void,
trigger: () => void,
) => {
get: () => T
set: (value: T) => void
}
CustomRefImpl源码
class CustomRefImpl<T> {
public dep?: Dep = undefined
private readonly _get: ReturnType<CustomRefFactory<T>>['get']
private readonly _set: ReturnType<CustomRefFactory<T>>['set']
public readonly __v_isRef = true
constructor(factory: CustomRefFactory<T>) {
const { get, set } = factory(
() => trackRefValue(this),
() => triggerRefValue(this),
)
this._get = get
this._set = set
}
get value() {
return this._get()
}
set value(newVal) {
this._set(newVal)
}
}
CustomRefImpl类中主要做了两件事,依赖收集trackRefValue和派发更新triggerRefValue。
trackRefValue()
export function trackRefValue(ref: RefBase<any>) {
if (shouldTrack && activeEffect) {
ref = toRaw(ref)
trackEffect(
activeEffect,
(ref.dep ??= createDep(
() => (ref.dep = undefined),
ref instanceof ComputedRefImpl ? ref : undefined,
)),
__DEV__
? {
target: ref,
type: TrackOpTypes.GET,
key: 'value',
}
: void 0,
)
}
}
triggerRefValue()
export function triggerRefValue(
ref: RefBase<any>,
dirtyLevel: DirtyLevels = DirtyLevels.Dirty,
newVal?: any,
) {
ref = toRaw(ref)
const dep = ref.dep
if (dep) {
triggerEffects(
dep,
dirtyLevel,
__DEV__
? {
target: ref,
type: TriggerOpTypes.SET,
key: 'value',
newValue: newVal,
}
: void 0,
)
}
}
总结
ref的原理也是这样,依赖收集trackRefValue和派发更新triggerRefValue