web-dev-qa-db-fra.com

Comment implémenter l'anti-rebond dans Vue2?

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.

91
sm4

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)
}
117
Primoz Rome

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>
42
bendytree

Très simple sans lodash

  handleScroll: function() {
   if (this.timeout) clearTimeout(this.timeout); 
     this.timeout = setTimeout(() => {
       // your action
     }, 200);
  }
10
pshx

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"
>

(source)

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);
},
9
stallingOne

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 )

4
digout

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).

4
sm4

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>
3
Coreus

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>

0
Amir Khadem

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

0
roli roli

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.

0
Tanvir Hasan