我有一个筛选输入框,想要对一组项目进行筛选。由于列表内容较多,因此我想使用防抖技术来延迟应用筛选,以改善用户体验。这是我的输入框,它绑定到filterText上,用于对列表进行筛选。
<input type="text" v-model="state.filterText" />
我有一个筛选输入框,想要对一组项目进行筛选。由于列表内容较多,因此我想使用防抖技术来延迟应用筛选,以改善用户体验。这是我的输入框,它绑定到filterText上,用于对列表进行筛选。
<input type="text" v-model="state.filterText" />
我没有找到理想的解决方案,因为我想在模板中看到我的绑定,所以我决定分享我的解决方案。我编写了一个简单的防抖函数,并使用以下语法来绑定这个行为:
setup() {
...
function createDebounce() {
let timeout = null;
return function (fnc, delayMs) {
clearTimeout(timeout);
timeout = setTimeout(() => {
fnc();
}, delayMs || 500);
};
}
return {
state,
debounce: createDebounce(),
};
},
还有模板语法:
<input
type="text"
:value="state.filterText"
@input="debounce(() => { state.filterText = $event.target.value })"
/>
嗨,我第一次在这里回答问题,请尽情纠正我的答案,感激不尽。
我认为最漂亮、最轻量级的解决方案是创建一个全局指令,在您所有的表单中都可以随意使用。
首先,您需要创建带有指令的文件,例如 debouncer.js,然后创建防抖函数。
//debouncer.js
/*
This is the typical debouncer function that receives
the "callback" and the time it will wait to emit the event
*/
function debouncer (fn, delay) {
var timeoutID = null
return function () {
clearTimeout(timeoutID)
var args = arguments
var that = this
timeoutID = setTimeout(function () {
fn.apply(that, args)
}, delay)
}
}
/*
this function receives the element where the directive
will be set in and also the value set in it
if the value has changed then it will rebind the event
it has a default timeout of 500 milliseconds
*/
module.exports = function debounce(el, binding) {
if(binding.value !== binding.oldValue) {
el.oninput = debouncer(function(){
el.dispatchEvent(new Event('change'))
}, parseInt(binding.value) || 500)
}
}
当你定义好这个文件后,你可以转到你的main.js导入它并使用导出的函数。
//main.js
import { createApp } from 'vue'
import debounce from './directives/debounce' // file being imported
const app = createApp(App)
//defining the directive
app.directive('debounce', (el,binding) => debounce(el,binding))
app.mount('#app')
完成了,当你想在输入框上使用指令时,只需像这样做,不需要任何导入或其他操作。
//Component.vue
<input
:placeholder="filter by name"
v-model.lazy="filter.value" v-debounce="400"
/>
如果您选择这种方式,那么v-model.lazy指令非常重要,因为默认情况下它会在输入事件上更新绑定属性,但设置此项将使其等待更改事件,而这是我们在防抖函数中发出的事件。这样做将阻止v-model自行更新,直到您停止编写或超时时间结束(可以在指令的值中设置)。
update:model-value
时,$event
只是新值,而不像原生html组件一样是js事件对象。因此,当我监听@change
时,我得到了js事件,但我无法访问新值。有什么解决办法吗?我尝试添加debouncer(function(inputEvent){....})
,但似乎并不是从组件发出的事件。我认为需要用@update:model-value
的等效物替换el.oninput
,而不是dom的事件监听器,但我不知道该怎么做。 - danbars对于一些更简单的解决方案,您可以使用流行的库:
<template>
<input type="text" v-model="searchText" @input="onInput" />
</template>
<script setup>
import debounce from "lodash/debounce"
const onInput = debounce(() => {
console.log(searchText.value)
}, 500)
</script>
<template>
<input type="text" v-model="searchText" @input="onInput" />
</template>
<script setup>
import { useDebounceFn } from "@vueuse/core"
const onInput = useDebounceFn(() => {
console.log(searchText.value)
}, 500)
</script>
<script setup>
import { ref, watch } from 'vue'
import debounce from 'lodash.debounce'
const searchTerms = ref('')
const getFilteredResults = async () => {
try {
console.log('filter changed')
// make axios call here using searchTerms.value
} catch (err) {
throw new Error(`Problem filtering results: ${err}.`)
}
}
const debouncedFilter = debounce(getFilteredResults, 250) // 250ms delay
watch(() => searchTerms.value, debouncedFilter)
</script>
<template>
<input v-model="searchTerms" />
</template>
<input @input="updateValue"/>
const updateValue = (event) => {
const timeoutId = window.setTimeout(() => {}, 0);
for (let id = timeoutId; id >= 0; id -= 1) {
window.clearTimeout(id);
}
setTimeout(() => {
console.log(event.target.value)
}, 500);
};
<template>
<input type="text" :value="name" @input="test" />
<span>{{ name }}</span>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
function debounce<T> (fn: T, wait: number) {
let timer: ReturnType<typeof setTimeout>
return (event: Event) => {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
if (typeof fn === 'function') {
fn(event)
}
}, wait)
}
}
export default defineComponent({
setup () {
const name = ref('test')
function setInputValue (event: Event) {
const target = event.target as HTMLInputElement
name.value = target.value
}
const test = debounce(setInputValue, 1000)
return { name, test }
}
})
</script>
https://www.npmjs.com/package/vue-debounce 现在支持 vue 3,也可以通过 composition API 进行注册,如下所示
setup() {
...
},
directives: {
debounce: vue3Debounce({ lock: true })
}
import { customRef } from 'vue'
export function useDebouncedRef(value, delay = 200) {
let timeout
return customRef((track, trigger) => {
return {
get() {
track()
return value
},
set(newValue) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}
})
}
组件中的使用方法:
<script setup>
import { useDebouncedRef } from './debouncedRef'
const text = useDebouncedRef('hello')
</script>
<template>
<input v-model="text" />
</template>
setup
返回防抖方法,这样模板只需使用@input="debouncedFilter"
。在setup
中,返回debouncedFilter: createDebounce((evt)=>{state.filterText=evt.target.value})
。 - danbarsinput @input.debounce
? - anjanesh