J'ai un simple champ de saisie dans un template Vue et j'aimerais utiliser debounce plus ou moins comme ceci:
<input type="text" v-model="filterKey" debounce="500">
Cependant, la propriété debounce
a été obsolète dans Vue 2 . La recommandation indique seulement: "utilisez v-on: entrée + fonction anti-débrayage par une tierce partie".
Comment la mettez-vous correctement en œuvre?
J'ai essayé de l'implémenter en utilisant lodash , v-on: input et v-model , mais je me demande s’il est possible de se passer de la variable supplémentaire.
Dans le modèle:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
En script:
data: function () {
return {
searchInput: '',
filterKey: ''
}
},
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
La clé de filtre est ensuite utilisée plus tard dans computed
props.
J'utilise debounce package NPM et implémenté comme ceci:
<input @input="debounceInput">
methods: {
debounceInput: debounce(function (e) {
this.$store.dispatch('updateInput', e.target.value)
}, config.debouncers.default)
}
En utilisant lodash et l'exemple de la question, l'implémentation ressemble à ceci:
<input v-on:input="debounceInput">
methods: {
debounceInput: _.debounce(function (e) {
this.filterKey = e.target.value;
}, 500)
}
Assigner un anti-rebond dans methods
peut être gênant. Donc au lieu de cela:
// Bad
methods: {
foo: _.debounce(function(){}, 1000)
}
Vous pouvez essayer:
// Good
created () {
this.foo = _.debounce(function(){}, 1000);
}
Cela devient un problème si vous avez plusieurs instances d'un composant - de la même manière que data
devrait être une fonction qui renvoie un objet. Chaque instance a besoin de sa propre fonction anti-rebond si elle est supposée agir de manière indépendante.
Voici un exemple du problème:
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>
Très simple sans lodash
handleScroll: function() {
if (this.timeout) clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
// your action
}, 200);
}
J'ai eu le même problème et cela a fonctionné sans plugins .
Puisque <input v-model="xxxx">
est exactement le même que
<input
v-bind:value="xxxx"
v-on:input="xxxx = $event.target.value"
>
J'ai pensé que je pouvais définir une fonction anti-rebond sur l'attribution de xxxx dans xxxx = $event.target.value
comme ça
<input
v-bind:value="xxxx"
v-on:input="debounceSearch($event.target.value)"
>
méthodes:
debounceSearch(val){
if(search_timeout) clearTimeout(search_timeout);
var that=this;
search_timeout = setTimeout(function() {
that.xxxx = val;
}, 400);
},
Réutilisable et sans 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>
(inspiré par minuscule rebounce )
Veuillez noter que j'ai posté cette réponse avant la réponse acceptée. Ce n'est pas correct. C'est juste un pas en avant par rapport à la solution dans la question. J'ai modifié la question acceptée pour montrer à la fois l'implémentation de l'auteur et l'implémentation finale que j'avais utilisée.
D'après les commentaires et le document de migration lié , j'ai apporté quelques modifications au code:
Dans le modèle:
<input type="text" v-on:input="debounceInput" v-model="searchInput">
En script:
watch: {
searchInput: function () {
this.debounceInput();
}
},
Et la méthode qui définit la clé de filtre reste la même:
methods: {
debounceInput: _.debounce(function () {
this.filterKey = this.searchInput;
}, 500)
}
Cela ressemble à un appel de moins (juste le v-model
, et non le v-on:input
).
Si vous avez besoin d’une approche très minimaliste à cet égard, j’en ai créé une (initialement fournie par Vuejs-tips pour supporter également IE) qui est disponible ici: https://www.npmjs.com/package/v-debounce
Usage:
<input v-model.lazy="term" v-debounce="delay" placeholder="Search for something" />
Puis dans votre composant:
<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>
Si vous utilisez Vue, vous pouvez également utiliser v.model.lazy
au lieu de debounce
, mais rappelez-vous que v.model.lazy
ne fonctionnera pas toujours car Vue ne le limite pas pour la personnalisation. Composants.
Pour les composants personnalisés, vous devez utiliser :value
avec @change.native
<b-input :value="data" @change.native="data = $event.target.value" ></b-input>
Si vous devez appliquer un délai dynamique avec la fonction debounce
de lodash:
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) {
// ...
}
}
et le modèle:
<template>
//...
<input type="text" v-model="search" @input="valueChanged" />
//...
</template>
NOTE: Dans l'exemple ci-dessus, j'ai créé un exemple de recherche permettant d'appeler l'API avec un délai personnalisé fourni dans props
Nous pouvons faire avec en utilisant quelques lignes de code JS:
if(typeof window.LIT !== 'undefined') {
clearTimeout(window.LIT);
}
window.LIT = setTimeout(() => this.updateTable(), 1000);
Solution simple! Travail parfait! J'espère que cela vous sera utile.