This article is translated

How to Debounce and Throttle Callbacks in Vue

Originally by Dmitri Pavlutin

Original address: dmitripavlutin.com/vue-debounc…

Be careful when listening for frequently triggered events, such as user typing in an input box, window resizing, scrolling, and Intersection Observer events.

These events are always triggered frequently, perhaps every few seconds. It is obviously unwise to make a FETCH request (or something similar) for every event.

What we need to do is slow down the event handler. The buffering techniques are debounce and throttle.

In this article, you will learn how to use The Anti-shake and throttling Control Watchers and event handlers in Vue components.

1. Observer stabilization

Let’s start with a simple component. Our task is to print the text the user entered into the text box to the console:

<template>
  <input v-model="value" type="text" />
  <p>{{ value }}</p>
</template>
<script>
export default {
  data() {
    return {
      value: ""}; },watch: {
    value(newValue, oldValue) {
      console.log("Value changed: ", newValue); }}};</script>
Copy the code

Open the demo

Open demo and type a few characters in the input box. Each time you enter, the value is logged to the console.

We print the log by using the Watcher to listen for the value data attribute. But if you want to include a GET request that takes value as an argument in the observer callback, you shouldn’t expect to make requests too often.

Let’s take a look at the behavior of printing console logs. The idea is to create an anti-shake function and then call that function from within the observer.

I have chosen the ‘Lodash. Debounce’ stabilization implementation here, but you are free to choose your preferred implementation.

Let’s apply the stabilization logic to the component:

<template>
  <input v-model="value" type="text" />
  <p>{{ value }}</p>
</template>
<script>
import debounce from "lodash.debounce";
export default {
  data() {
    return {
      value: ""}; },watch: {
    value(. args) {
      this.debouncedWatch(... args); }},created() {
    this.debouncedWatch = debounce((newValue, oldValue) = > {
      console.log('New value:', newValue);
    }, 500);
  },
  beforeUnmount() {
    this.debouncedWatch.cancel(); }};</script>
Copy the code

Try the demo

If you open the demo, you’ll see that from the user’s point of view, not much has changed: you can still type characters as you did in the previous demo.

But there is one difference: the new input value is not logged to the console until 500ms after the last input. That means the anti-shake is working.

There are only 3 simple steps to implement observer stabilization:

  1. increate()Hook to create a shake-off callback and assign it to the instance:this.debouncedWatch = debounce(... , 500)..
  2. In the observer callbackwatch.value() { ... }Pass in the correct arguments to thethis.debouncedWatch().
  3. In the end,beforeUnmount()Call from hookthis.debouncedWatch.cancel()Before uninstalling the component, cancel all pending anti-shake functions.

In the same way, you can apply stabilization to observers of any data attribute. You can then safely perform some of the heavier operations inside the shake back, such as network requests, heavy DOM operations, and so on.

2. The event processor is shaken

In the previous section, I showed you how to use stabilization for observers, but what about a regular event handler?

We reuse the previous example of the user entering data into the input field, but this time we will add an event handler to the input field.

As usual, if you do not take any buffering measures, the value will be printed to the console whenever it is changed:

<template>
  <input v-on:input="handler" type="text" />
</template>
<script>
export default {
  methods: {
    handler(event) {
      console.log('New value:', event.target.value); }}};</script>
Copy the code

Try the demo

Open the demo and type a few characters in the input box. Look at the console: you’ll notice that a log is printed every time you type.

Similarly, if you are performing heavy operations (such as network requests), this is not a good idea.

To use stabilization for event handlers, see the following:

<template>
  <input v-on:input="debouncedHandler" type="text" />
</template>
<script>
import debounce from "lodash.debounce";
export default {
  created() {
    this.debouncedHandler = debounce(event= > {
      console.log('New value:', event.target.value);
    }, 500);
  },
  beforeUnmount() {
    this.debouncedHandler.cancel(); }};</script>
Copy the code

Try the demo

Open demo and type some characters. The component does not log the new input value to the console until 500ms after the last input. Once again, the anti-shake works!

The anti-shake implementation of the event handler requires only 3 steps:

  1. . Increate()Hook, immediately after the instance is created, the anti-shake callbackdebounce(event => {... }, 500).Assign values to thethis.debouncedHandler
  2. In the input box templatev-on:inputassigndebouncedHandler<input v-on:input="debouncedHandler" type="text" />
  3. Finally, before uninstalling the component, inbeforeUnmount()Call from hookthis.debouncedHandler.cancel()Cancel all pending function calls.

On the other hand, these examples use anti-shake techniques. However, the same approach can be used to create throttling functions.

3. Pay attention to

You might wonder: why not just create the buffering functions in the method option of the component and then call those methods in the Template as event handlers?

/ /... methods: { // Why not? debouncedHandler: debounce(function () { ... }}, 500)} //...Copy the code

This is much simpler than creating an anti-shake function on the instance object.

Such as:

<template>
  <input v-on:input="debouncedHandler" type="text" />
</template>
<script>
import debounce from "lodash.debounce";
export default {
  methods: {
    // Don't do this!
    debouncedHandler: debounce(function(event) {
      console.log('New value:', event.target.value);
    }, 500)}};</script>
Copy the code

Try the demo

Instead of creating a created() hook, we assign the created callback to methods.debouncedHandler.

If you have tried the demo, you will find that it works!

The problem is that the component uses export default {… } Exported Options objects, including methods, are reused by component instances.

If there are more than two component instances in a web page, all components will apply the same stabilization function methods.debouncedHandler – this will cause the stabilization to fail.

4. To summarize

In Vue, you can easily apply stabilization and throttling to observers and event handlers.

The core logic is to create a buffered or throttled callback in the Created () hook and assign it to the instance.

// ...
  created() {
    this.debouncedCallback = debounce((. args) = > {
      // The debounced callback
    }, 500);
  },
// ...
Copy the code

A) Then call the anti-shake function on the instance inside the observer:

// ...
  watch: {
    value(. args) {
      this.debouncedCallback(... args); }},// ...
Copy the code

B) or set an event handler in template:

<template>
  <input v-on:input="debouncedHandler" type="text" />
</template>
Copy the code

After that, each call to this.debouncedCallback(… Args), the internal callback can be buffered even at very high frequencies.

Do you have any questions about anti – shake and throttling in Vue? Questions are welcome!