js防抖和节流

166 阅读2分钟

直接步入正题:

防抖和节流就是限制函数的执行次数

举个栗子:

  <div class="box">
    <input type="text">
    <input type="submit" id="inputId">
  </div>
  <script>
    const btn = document.getElementById('inputId');
    btn.addEventListener('click', submit)
    function submit() {
      console.log('接口数据');
    }
  </script>

在用户点击的时候他会重复提交,所以就需要限制submit的执行次数

防抖:

在第一次触发事件时,不立即执行函数,而是给出一个期限值比如200ms,然后:

  • 如果在2000ms内没有再次触发滚动事件,那么就执行函数
  • 如果在2000ms内再次触发滚动事件,那么当前的计时取消,重新开始计时

效果:如果短时间内大量触发同一事件,只会执行一次函数。

  <button>防抖</button>
  <script>
    let btn = document.querySelector("button")
    function submit() {
      console.log('请求数据');
    }
    function debounce(fn, time) {
      var timer
      return function () {
        clearInterval(timer)
        timer = setTimeout(fn, time)
      }
    }
    btn.addEventListener("click", debounce(submit, 2000))
  </script>

到了这一步防抖已经实现了

节流:

效果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效

  <button>节流</button>
  <script>
    let btn = document.querySelector("button")
    function submit() {
      console.log('请求数据');
    }
    function throttle(fn, delay) {
      var time1 = Date.now()
      return function () {
        var time2 = Date.now()
        if (time2 - time1 > delay) {
          fn()
          time1 = Date.now()
        } else {
          console.log('正在请求中');
        }
      }
    }
    btn.addEventListener("click", throttle(submit, 2000))
  </script>

这样就实现了函数的节流

vue中函数节流和防抖

第一步:定义公共防抖和节流函数

export default {
 
  /**
   * 函数防抖
   * 触发事件后在n秒后执行,如果n秒内又触发事件,则重新计算时间
   */
  debounce(fn, wait = 1000) {
    let timer;
    return function () {
      let context = this;
      let args = arguments;
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        fn.apply(context, args);
      }, wait)
    }
  },
 
  /**
   * 函数节流
   * 触发事件立即执行,但在n秒内连续触发则不执行
   */
  throttle(fn, wait = 1000) {
    let timer;
    return function () {
      if (timer != null) return;
      let context = this;
      let args = arguments;
      fn.apply(context, args);
      timer = setTimeout(() => {
        timer = null;
      }, wait);
    }
  },
}

注意
防抖和节流函数中 return 的函数不能使用箭头函数,如果使用箭头函数则 this 就会指向 globalFunction,就会有问题

第二步:新建 Vue 组件

<template>
  <div class="wrapper">
    <div @click="btnDebounce('函数','防抖')">函数防抖</div>
    <div @click="btnThrottle('函数','节流')">函数节流</div>
  </div>
</template>

第三步:引用 globalFunction 并在 methods 中使用

<script>
  import globalFunction from "../../utils/globalFunction";
 
  export default {
    name: "test",
    methods: {
 
      btnDebounce: globalFunction.debounce(function (str1, str2) {
        console.log(str1, str2);
      }, 2000),
 
      btnThrottle: globalFunction.throttle(function (str1, str2) {
        console.log(str1, str2);
      }, 2000),
 
    }
  }
</script>

说明:
globalFunction 类的 debounce、throttle 返回的一个函数,就相当于

btnDebounce() {
   let context = this;
   let args = arguments;
   console.log(this);
   console.log(args);
 }

所以可以拿到当前 this 和 arguments 参数,因为 argument 获取的是一个类似数组的对象,所以可以通过调用函数的 apply 方法来传递参数

TS+Vue3中使用

methods.ts

export const throttle = <T extends (...args: any[]) => void>(
  func: T,
  wait: number
) => {
  let timer: NodeJS.Timeout | null = null;
  return function (this: any, ...args: any[]) {
    if (!timer) {
      func.apply(this, args);
      timer = setTimeout(() => {
        timer = null;
      }, wait);
    }
  } as T;
};

xxx.vue

import { throttle } from "@/utils/methods";
const itemClick = throttle(function () {

}, 2000);