自定义ref实现防抖

该笔记的内容主要来自本人在学习渡一课程的配套资料和个人总结而成

自定义ref实现防抖

首先是一个防抖最基本的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div class="container">
<input @input="debounceInputHandler" type="text" />
<p class="result">{{ text }}</p>
</div>
</template>

<script setup>
import { ref } from 'vue'
import { debounce } from 'lodash'
const text = ref('')

function inputHandler(e) {
text.value = e.target.value
}

const debounceInputHandler = debounce(inputHandler, 1000)
</script>

假设 Vue 给我们提供了一个防抖的 ref

1
2
3
4
5
6
7
8
9
10
11
<template>
<div class="container">
<input v-model="text" type="text" />
<p class="result">{{ text }}</p>
</div>
</template>

<script setup>
import { debounceRef } from 'vue'
const text = debounceRef('', 1000)
</script>

然而上面的设想是美好的,代码能够简洁很多,但是 Vue 并没有给我们提供 debounceRef.

不过自己可以借助 Vue内置API customRef 来实现这个功能。customRef 的定义如下:

1
2
3
4
5
6
7
8
9
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>

type CustomRefFactory<T> = (
track: () => void,
trigger: () => void
) => {
get: () => T
set: (value: T) => void
}

下面是 customRef 的一个基本使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { customRef } from 'vue'
let value = ''
const text = customRef(() => {
return {
get() {
console.log('get')
return value
},
set(val) {
value = val
console.log('set')
}
}
})
console.log(text)
console.log(text.value)
text.value = 'test'

通过 customRef 实现 ref 原有的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>
<div class="container">
<input v-model="text" type="text" />
<p class="result">{{ text }}</p>
</div>
</template>

<script setup>
import { customRef } from 'vue'
let value = '111'
const text = customRef((track, trigger) => {
return {
get() {
track()
console.log('get方法被调用')
return value
},
set(val) {
trigger()
console.log('set方法被调用')
value = val
}
}
})
</script>

下面是通过自定义ref来实现防抖:

debounceRef.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { customRef } from 'vue'
import { debounce } from 'lodash'
export function debounceRef(value, delay = 1000) {
return customRef((track, trigger) => {
let _value = value

const _debounce = debounce((val) => {
_value = val
trigger() // 派发更新
}, delay)

return {
get() {
track() // 收集依赖
return _value
},
set(val) {
_debounce(val)
}
}
})
}

更优雅的方式

debounceRef.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { customRef } from 'vue'

export function useDebouncedRef(value, delay = 200) {
let timeId
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
if(timeId) clearTimeout(timeId)
timeId = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}