How to implement debounce in Vue2?
I am using debounce NPM package and implemented like this:
<input @input="debounceInput">
methods: { debounceInput: debounce(function (e) { this.$store.dispatch('updateInput', e.target.value) }, config.debouncers.default)}
Using lodash and the example in the question, the implementation looks like this:
<input v-on:input="debounceInput">
methods: { debounceInput: _.debounce(function (e) { this.filterKey = e.target.value; }, 500)}
Option 1: Re-usable, no deps
- Recommended if needed more than once in your project
/helpers.js
export 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) }}
/Component.vue
<script> import {debounce} from './helpers' export default { data () { return { input: '', debouncedInput: '' } }, watch: { input: debounce(function (newVal) { this.debouncedInput = newVal }, 500) } }</script>
Option 2: In-component, also no deps
- Recommended if using once or in small project
/Component.vue
<template> <input type="text" v-model="input" /></template><script> export default { data: { timeout: null, debouncedInput: '' }, computed: { input: { get() { return this.debouncedInput }, set(val) { if (this.timeout) clearTimeout(this.timeout) this.timeout = setTimeout(() => { this.debouncedInput = val }, 300) } } } }</script>
Assigning debounce in methods
can be trouble. So instead of this:
// Badmethods: { foo: _.debounce(function(){}, 1000)}
You may try:
// Goodcreated () { this.foo = _.debounce(function(){}, 1000);}
It becomes an issue if you have multiple instances of a component - similar to the way data
should be a function that returns an object. Each instance needs its own debounce function if they are supposed to act independently.
Here's an example of the problem:
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>