Как реализовать debounce в Vue2?
У меня есть простое поле ввода в шаблоне Vue, и я хотел бы использовать debounce более или менее следующим образом:
<input type="text" v-model="filterKey" debounce="500">
Однако свойство debounce
устарело в Vue 2. В рекомендации говорится: "использовать v-on: вход + функция дебаза третьей стороны".
Как вы его правильно реализуете?
Я попытался реализовать его с помощью lodash, v-on: input и v-model, но мне интересно, можно ли обойтись без дополнительной переменной.
В шаблоне:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
В сценарии:
data: function () {
return {
searchInput: '',
filterKey: ''
}
},
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
Затем фильтр-ключ используется позже в computed
реквизитах.
Ответы
Ответ 1
Я использую debounce пакет NPM и реализован следующим образом:
<input @input="debounceInput">
methods: {
debounceInput: debounce(function (e) {
this.$store.dispatch('updateInput', e.target.value)
}, config.debouncers.default)
}
Используя lodash и пример в вопросе, реализация выглядит следующим образом:
<input v-on:input="debounceInput">
methods: {
debounceInput: _.debounce(function (e) {
this.filterKey = e.target.value;
}, 500)
}
Ответ 2
Назначение debounce в methods
может быть проблемой. Итак, вместо этого:
// Bad
methods: {
foo: _.debounce(function(){}, 1000)
}
Вы можете попробовать:
// Good
created () {
this.foo = _.debounce(function(){}, 1000);
}
Это становится проблемой, если у вас есть несколько экземпляров компонента - подобно тому, как data
должны быть функцией, которая возвращает объект. Каждому экземпляру нужна своя функция debounce, если предполагается, что они действуют независимо.
Вот пример проблемы:
Vue.component('counter', {
template: '<div>{{ i }}</div>',
data: function(){
return { i: 0 };
},
methods: {
// DON'T DO THIS
increment: _.debounce(function(){
this.i += 1;
}, 1000)
}
});
new Vue({
el: '#app',
mounted () {
this.$refs.counter1.increment();
this.$refs.counter2.increment();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.min.js"></script>
<div id="app">
<div>Both should change from 0 to 1:</div>
<counter ref="counter1"></counter>
<counter ref="counter2"></counter>
</div>
Ответ 3
У меня была такая же проблема, и это работало без плагинов.
Поскольку <input v-model="xxxx">
точно такой же, как
<input
v-bind:value="xxxx"
v-on:input="xxxx = $event.target.value"
>
(источник)
Я подумал, что мог бы установить функцию debounce при назначении xxxx в xxxx = $event.target.value
как это
<input
v-bind:value="xxxx"
v-on:input="debounceSearch($event.target.value)"
>
методы:
debounceSearch(val){
if(search_timeout) clearTimeout(search_timeout);
var that=this;
search_timeout = setTimeout(function() {
that.xxxx = val;
}, 400);
},
Ответ 4
Очень просто без лодаш
handleScroll: function() {
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
// your action
}, 200);
}
Ответ 5
Повторное использование и без deps:
helpers.js
module.exports = function debounce (fn, delay) {
var timeoutID = null
return function () {
clearTimeout(timeoutID)
var args = arguments
var that = this
timeoutID = setTimeout(function () {
fn.apply(that, args)
}, delay)
}
}
.vue
<script>
import debounce from './helpers'
export default {
data () {
return {
input: '',
debouncedInput: ''
}
},
watch: {
input: debounce(function (newVal) {
this.debouncedInput = newVal
}, 500)
}
}
</script>
(вдохновлено крошечным дебатом)
Ответ 6
Обратите внимание, что я отправил этот ответ перед принятым ответом. Это не правильно. Это всего лишь шаг вперед от решения в вопросе. Я отредактировал принятый вопрос, чтобы показать как реализацию автора, так и конечную реализацию, которую я использовал.
Основываясь на комментариях и связанном документе миграции, я внес несколько изменений в код:
В шаблоне:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
В сценарии:
watch: {
searchInput: function () {
this.debounceInput();
}
},
И метод, который устанавливает ключ фильтра, остается неизменным:
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
Похоже, что есть еще один вызов (просто v-model
, а не v-on:input
).
Ответ 7
Если вам нужен минималистичный подход к этому, я сделал один (изначально разработанный для vuejs-tips для поддержки IE), который доступен здесь: https://www.npmjs.com/package/v-debounce
Применение:
<input v-model.lazy="term" v-debounce="delay" placeholder="Search for something" />
Затем в вашем компоненте:
<script>
export default {
name: 'example',
data () {
return {
delay: 1000,
term: '',
}
},
watch: {
term () {
// Do something with search term after it debounced
console.log('Search term changed to ${this.term}')
}
},
directives: {
debounce
}
}
</script>
Ответ 8
Если вы используете Vue, вы также можете использовать v.model.lazy
вместо debounce
но помните, что v.model.lazy
не всегда будет работать, поскольку Vue ограничивает его для пользовательских компонентов.
Для пользовательских компонентов вы должны использовать :value
вместе с @change.native
<b-input :value="data" @change.native="data = $event.target.value" ></b-input>
Ответ 9
Мы можем сделать с помощью нескольких строк кода JS:
if(typeof window.LIT !== 'undefined') {
clearTimeout(window.LIT);
}
window.LIT = setTimeout(() => this.updateTable(), 1000);
Простое решение! Идеальная работа! Надеюсь, будет полезно для вас, ребята.
Ответ 10
В случае, если необходимо применить динамическую задержку с lodash debounce
функции:
props: {
delay: String
},
data: () => ({
search: null
}),
created () {
this.valueChanged = debounce(function (event) {
// Here you have access to 'this'
this.makeAPIrequest(event.target.value)
}.bind(this), this.delay)
},
methods: {
makeAPIrequest (newVal) {
// ...
}
}
И шаблон:
<template>
//...
<input type="text" v-model="search" @input="valueChanged" />
//...
</template>
ПРИМЕЧАНИЕ: в приведенном выше примере я сделал пример поискового ввода, который может вызывать API с пользовательской задержкой, которая указана в props
Ответ 11
Хотя почти все ответы здесь уже верны, если кто-то ищет быстрое решение, у меня есть для этого директива.
https://www.npmjs.com/package/vue-lazy-input
Это относится к @input и v-модели, поддерживает пользовательские компоненты и элементы DOM, debounce и throttle.
Vue.use(VueLazyInput)
new Vue({
el: '#app',
data() {
return {
val: 42
}
},
methods:{
onLazyInput(e){
console.log(e.target.value)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/lodash/lodash.min.js"></script><!-- dependency -->
<script src="https://unpkg.com/[email protected]"></script>
<div id="app">
<input type="range" v-model="val" @input="onLazyInput" v-lazy-input /> {{val}}
</div>