面试官:你连防抖和节流都不知道??
作者:大海是蓝色blue
🐟防抖
防抖就是单位时间内,频繁触发事件,只执行一次。
举个栗子,他就像王者荣耀的回城机制一样,被打断了就需要重新进行
使用场景
- 搜索框搜索输入。只需用户最后一次输完,再发送请求
- 手机号,邮箱输入验证
我们通过一个案例来认识防抖利用防抖来处理-鼠标滑过盒子显示文字
需求:鼠标在盒子上移动,里面的数字就会加一.下面是代码实现
<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>
只要移动鼠标,数字就会一直加,频繁触发更新,这可能会引发不必要的计算和 DOM 更新,消耗大量的 CPU 和内存资源,从而影响页面的性能和响应速度。这个时候用上防抖就可以实现性能优化。
我们现在需要鼠标停止500ms之后,数字才会变化+1
我们怎么实现这个防抖呢,有两种方式
- lodash提供的防抖来处理
- 手写一个防抖函数来处理
<顺便吆喝一句,技术大厂内推,前/后端or测试机会,尤其东莞、深圳等地紧缺!感兴趣来>→这里
🐟我们先来介绍lodash提供的防抖
怎么引入呢?
一、使用 npm 或 yarn 安装并全局引入
- 使用 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的官方文档
使用说明如下
_.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
的值。
二、脚本部分
- 引入依赖:
import { ref } from "vue"; import _ from "lodash";
从 Vue 中引入 ref
函数用于创建响应式数据,从 Lodash 库中引入 _
,以便使用其中的防抖功能。
- 定义响应式数据和防抖函数引用:
let a = ref(0); let debouncedFunction;
a
是一个使用ref
创建的响应式数据,初始值为 0,用于记录自增的数值。debouncedFunction
是一个变量,用于存储防抖后的函数引用。
- 定义普通自增方法和防抖触发方法:
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)来实现的,核心思路如下
- 先创建一个定时器变量
- 每次事件触发都需要先判断是否有定时器,如果有先清除以前的定时器
- 如果没有定时器,则开始定时器,存入到定时器变量里面
- 定时器里面写函数调用
function debounce(func, delay) { let timer; //1. 先创建一个定时器变量 return function() { if (timer) { //2. 每次事件触发都需要先判断是否有定时器,如果有先清除以前的定时器 clearTimeout(timer); } timer = setTimeout(() => { //3.则开始定时器,存入到定时器变量里面 func() //4.定时器里面写函数调用 }, delay); }; }
下次遇到面试官问这个,就再也不怕啦
🐟节流
单位时间内,频繁触发事件,只执行一次举个栗子:
- 王者荣耀技能冷却
- 和平精英98k换子弹的时候不能射击
和防抖一样节流也有两种实现方法,一个是用lodash里的throttle,另一种是手搓一个节流函数自己实现.
下面是lodash的官方文档的解释
_.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;
:使用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实现的,下面是核心思路
- 先要创建一个定时器变量
- 每当鼠标移动,事件触发都要判断是否有定时器了,如果有就不开启新的定时器
- 如果没有定时器,则开始定时器,记得存到变量里 4.定时器里调用执行的函数 5.定时器里要把定时器清空
我们来根据上述的思路实现一下
function throttle(fn, t) { let timer = null //创建一个定时器变量 return function () { //记得返回 if (!timer) { //如果不存在定时器就开启定时器,放入变量里 timer = setTimeout(() => { fn() //定时器里调用函数 timer = null //定时器里要把定时器置空 }, 1000); } } }
如果不将定时器置空,那么在定时器触发一次后,它会一直存在,后续即使再次触发节流函数,由于定时器已经存在,不会再创建新的定时器,这就可能导致在很长一段时间内目标函数都不会再次被执行,与节流的预期行为不符。
为什么这里的清除定时器使用null,而不是clearTimeout()呢,我们需要知道我们是在setTimeout里面,去置空,但是因为定时器还在运作,所以我们不可以用clearTimeout()去删除
🐟END
希望大家从此以后,如果遇到面试官问到防抖和节流,可以轻易秒杀。
#牛客创作赏金赛##牛客在线求职答疑中心##牛客解忧铺##面试时最害怕被问到的问题#