Published 2021-11-20

如何在 vue 中使用 Debounce、Throttle

为什么要使用 debounce 和 throttle

防抖和节流就是针对响应跟不上触发频率这类问题的两种解决方案。在给 DOM 绑定事件时,有些事件我们是无法控制触发频率的。 如鼠标移动事件 onmousemove, 滚动滚动条事件 onscroll,窗口大小改变事件 onresize,瞬间的操作都会导致这些事件会被高频触发。 如果事件的回调函数较为复杂,就会导致响应跟不上触发,出现页面卡顿,假死现象。 在实时检查输入时,如果我们绑定 onkeyup 事件发请求去服务端检查,用户输入过程中,事件的触发频率也会很高,会导致大量的请求发出,响应速度会大大跟不上触发。

在 watcher 中使用 debounce

  1. created生命周期将this.debounce绑定到this上,这样this.debounce就可以在created生命周期中使用。
  2. watch函数中,使用this.debounce调用debounce函数,并传入this.debounce的回调函数。
  3. 在事件销毁前,使用this.debounce调用clear函数,清除定时器。
<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>

使用 debounce 处理事件

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

为什么不适用debouncethrottle直接绑定函数呢?比如:

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

使用export default { ... }从组件导出的 options 对象,包括方法,将被组件的所有实例重用。如果网页中有 2 个或更多的组件实例,那么所有的组件都将使用相同的debouncethrottle的函数方法。

总结

vue 中使用 debouncethrottle 函数,应该在 createdbeforeUnmount 生命周期中使用,而不是在 watch 中使用。

<template>
  <input v-on:input="debouncedHandler" type="text" />
</template>
<script>
  export default {
    created() {
      this.debouncedCallback = debounce((...args) => {
        // The debounced
        callback
      }, 500)
    },
    watch: {
      value(...args) {
        this.debouncedCallback(...args)
      }
    }
  }
</script>