🐟防抖
它确保某个函数在一段时间内只执行一次,只有当触发事件后等待了指定的毫秒数且没有再次触发时,才会执行该函数。
举个栗子,他就像王者荣耀的回城机制一样,被打断了就需要重新进行
使用场景
- 搜索框搜索输入。只需用户最后一次输完,再发送请求
- 手机号,邮箱输入验证
我们通过一个案例来认识防抖 利用防抖来处理-鼠标滑过盒子显示文字
需求:鼠标在盒子上移动,里面的数字就会加一.下面是代码实现
<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提供的防抖来处理
- 手写一个防抖函数来处理
🐟我们先来介绍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(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实现的,下面是核心思路
- 先要创建一个定时器变量
- 每当鼠标移动,事件触发都要判断是否有定时器了,如果有就不开启新的定时器
- 如果没有定时器,则开始定时器,记得存到变量里 4.定时器里调用执行的函数 5.定时器里要把定时器清空
我们来根据上述的思路实现一下
function throttle(fn, t) {
let timer = null //创建一个定时器变量
return function () { //记得返回
if (!timer) { //如果不存在定时器就开启定时器,放入变量里
timer = setTimeout(() => {
fn() //定时器里调用函数
timer = null //定时器里要把定时器置空
}, 1000);
}
}
}
如果不将定时器置空,那么在定时器触发一次后,它会一直存在,后续即使再次触发节流函数,由于定时器已经存在,不会再创建新的定时器,这就可能导致在很长一段时间内目标函数都不会再次被执行,与节流的预期行为不符。
为什么这里的清除定时器使用null,而不是clearTimeout()呢,我们需要知道我们是在setTimeout里面,去置空,但是因为定时器还在运作,所以我们不可以用clearTimeout()去删除
🐟END
希望大家从此以后,如果遇到面试官问到防抖和节流,可以轻易秒杀