面试官:你连防抖和节流都不知道??

8,597 阅读7分钟

🐟防抖

它确保某个函数在一段时间内只执行一次,只有当触发事件后等待了指定的毫秒数且没有再次触发时,才会执行该函数。

image.png 举个栗子,他就像王者荣耀的回城机制一样,被打断了就需要重新进行

image.png

使用场景

  • 搜索框搜索输入。只需用户最后一次输完,再发送请求
  • 手机号,邮箱输入验证

我们通过一个案例来认识防抖 利用防抖来处理-鼠标滑过盒子显示文字

需求:鼠标在盒子上移动,里面的数字就会加一.下面是代码实现

<template>
  <div>
    <div class="box" @mousemove="addnum">{{ a }}</div>
  </div>
</template>

<script setup>
import { ref } from "vue";
import _ from "lodash";

let a = ref(0);
let debouncedFunction;

const addnum = () => {
  a.value++;
};

</script>

<style lang="css" scoped>
.box {
  width: 200px;
  height: 200px;
  background-color: antiquewhite;
}
</style>

image.png

只要移动鼠标,数字就会一直加,频繁触发更新,这可能会引发不必要的计算和 DOM 更新,消耗大量的 CPU 和内存资源,从而影响页面的性能和响应速度。这个时候用上防抖就可以实现性能优化。

我们现在需要鼠标停止500ms之后,数字才会变化+1

我们怎么实现这个防抖呢,有两种方式

  • lodash提供的防抖来处理
  • 手写一个防抖函数来处理

🐟我们先来介绍lodash提供的防抖

怎么引入呢?

一、使用 npm 或 yarn 安装并全局引入

  1. 使用 npm 安装 Lodash:
   npm install lodash

或者使用 yarn:

   yarn add lodash

2. 在项目的入口文件(通常是 main.js)中全局引入 Lodash:

   import Vue from 'vue';
   import App from './App.vue';
   import _ from 'lodash';

   Vue.prototype._ = _;

   new Vue({
     render: h => h(App)
   }).$mount('#app');

这样在整个项目的任何组件中都可以通过 this._ 来访问 Lodash 的函数。 引入了之后我们来使用这个lodash,下面是lodash的官方文档

image.png

使用说明如下

_.debounce(func, [wait=0], [options={}])

  • func:要进行防抖处理的目标函数。

  • wait:可选参数,防抖的等待时间,单位为毫秒。默认值为 0。

  • options:可选参数,一个包含配置选项的对象,主要有以下几个可配置的属性:

    • leading:布尔值,决定是否在防抖等待开始前立即调用一次函数。默认值为 false。
    • trailing:布尔值,决定是否在防抖等待结束后调用一次函数。默认值为 true。
    • maxWait:防抖函数等待的最大时间,如果在这个时间内没有触发新的调用,就会执行函数,即使等待时间还未结束。

来看我们的代码实现

一、模板部分

<template>
  <div>
    <div class="box" @mousemove="debouncedAddnum">{{ a }}</div>
  </div>
</template>

在模板中,有一个 <div> 元素,其类名为 "box",并绑定了 mousemove 事件到 debouncedAddnum 方法。同时,展示了一个数据 a 的值。

二、脚本部分

  1. 引入依赖:
   import { ref } from "vue";
   import _ from "lodash";

从 Vue 中引入 ref 函数用于创建响应式数据,从 Lodash 库中引入 _,以便使用其中的防抖功能。

  1. 定义响应式数据和防抖函数引用:
   let a = ref(0);
   let debouncedFunction;
  • a 是一个使用 ref 创建的响应式数据,初始值为 0,用于记录自增的数值。
  • debouncedFunction 是一个变量,用于存储防抖后的函数引用。
  1. 定义普通自增方法和防抖触发方法:
   const addnum = () => {
     a.value++;
   };

   const debouncedAddnum = () => {
     if (!debouncedFunction) {
       debouncedFunction = _.debounce(addnum, 500);
     }
     debouncedFunction();
   };
  • addnum 方法用于直接增加 a 的值。
  • debouncedAddnum 是在模板中绑定到鼠标移动事件的方法。当首次调用这个方法时,如果 debouncedFunction 为 null,则使用 _.debounce(addnum, 500) 创建一个 Lodash 的防抖函数,并将其存储在 debouncedFunction 中。之后每次调用 debouncedAddnum,都会调用存储的防抖函数,确保在鼠标移动事件频繁触发时,只有在一定时间间隔(这里是 500 毫秒)内没有新的事件触发才会执行 addnum 方法来增加 a 的值。

🐟再来手搓一个防抖函数

手搓防抖函数是面试官经常会问的,我们只需记住他的核心思路即可。

防抖的核心就是利用定时器(Settimeout)来实现的, 核心思路如下

  1. 先创建一个定时器变量
  2. 每次事件触发都需要先判断是否有定时器,如果有先清除以前的定时器
  3. 如果没有定时器,则开始定时器,存入到定时器变量里面
  4. 定时器里面写函数调用
function debounce(func, delay) {
  let timer;  //1. 先创建一个定时器变量
  return function() {
    if (timer) {   //2. 每次事件触发都需要先判断是否有定时器,如果有先清除以前的定时器
      clearTimeout(timer);
    }
        timer  =  setTimeout(() => {   //3.则开始定时器,存入到定时器变量里面
     func()    //4.定时器里面写函数调用
    }, delay);
  };
}

下次遇到面试官问这个,就再也不怕啦

🐟节流

是一种技术,它限制某个函数的执行频率,在指定的时间间隔内最多只执行一次,确保函数不会过于频繁地被调用。 举个栗子:

  1. 王者荣耀技能冷却
  2. 和平精英98k换子弹的时候不能射击

image.png

和防抖一样节流也有两种实现方法,一个是用lodash里的throttle,另一种是手搓一个节流函数自己实现.

下面是lodash的官方文档的解释 image.png _.throttle(func, [wait=0], [options={}])

使用说明如下

  • func:要进行节流处理的目标函数。

  • wait:可选参数,节流的时间间隔,单位为毫秒。默认值为 0。

  • options:可选参数,一个包含配置选项的对象,主要有以下几个可配置的属性:

    • leading:布尔值,决定是否在节流开始时立即调用一次函数。默认值为 true。
    • trailing:布尔值,决定是否在节流结束后调用一次函数。默认值为 true。

来康康我们的代码实现

一、模板部分(<template>

  • <div class="box" @mousemove="throttledFunction">{{ a }}</div>

    • 定义了一个具有 box 类名的 div 元素。

    • 当鼠标在这个 div 上移动时,会触发 throttledFunction 方法。

    • 同时,在这个 div 中会显示变量 a 的值。

二、脚本部分(<script setup>

  • import { ref } from "vue";:从 Vue 中引入 ref 函数,用于创建响应式数据。
  • import _ from 'lodash';:引入 Lodash 库,用于使用节流函数 _.throttle
  • let a = ref(0);:使用 ref 创建了一个响应式变量 a,初始值为 0。
  • function myFunction() { a.value++; }:定义了一个函数 myFunction,每次调用这个函数时,会将响应式变量 a 的值加 1。
  • const throttledFunction = _.throttle(myFunction, 1000);:使用 Lodash 的 _.throttle 函数对 myFunction 进行节流处理,设置节流时间间隔为 1000 毫秒(1 秒)。这意味着在 1 秒内,无论 throttledFunction 被触发多少次,myFunction 最多只会执行一次。

总体来说,这段代码创建了一个 Vue 组件,当鼠标在特定的 div 元素上移动时,会触发经过节流处理的函数,该函数会增加一个响应式变量的值,并在页面上显示这个变量的值。通过节流处理,避免了在鼠标快速移动时频繁执行函数,从而提高性能

接下来我们来手搓一个 .throttle函数

节流和防抖的手写非常相似,他们都是由setTimeout实现的,下面是核心思路

  1. 先要创建一个定时器变量
  2. 每当鼠标移动,事件触发都要判断是否有定时器了,如果有就不开启新的定时器
  3. 如果没有定时器,则开始定时器,记得存到变量里 4.定时器里调用执行的函数 5.定时器里要把定时器清空

我们来根据上述的思路实现一下


function throttle(fn, t) {
  let timer = null //创建一个定时器变量
  return function () {   //记得返回
    if (!timer) {   //如果不存在定时器就开启定时器,放入变量里
      timer = setTimeout(() => {
        fn()  //定时器里调用函数
        timer = null   //定时器里要把定时器置空
      }, 1000);
    }
  }
}

如果不将定时器置空,那么在定时器触发一次后,它会一直存在,后续即使再次触发节流函数,由于定时器已经存在,不会再创建新的定时器,这就可能导致在很长一段时间内目标函数都不会再次被执行,与节流的预期行为不符。

为什么这里的清除定时器使用null,而不是clearTimeout()呢,我们需要知道我们是在setTimeout里面,去置空,但是因为定时器还在运作,所以我们不可以用clearTimeout()去删除

🐟END

image.png 希望大家从此以后,如果遇到面试官问到防抖和节流,可以轻易秒杀