Appearance
一、防抖和节流
防抖和节流其实都是在规避频繁触发回调导致大量计算,从而影响页面发生抖动甚至卡顿。简单的说将多次回调比如页面点击或 ajax 调用变为一次。防抖和节流的区别在于以第一次为准还是最后一次为准。
二、常见的使用场景
三、节流 Throttle - 调用多次,只有第一次有效
基本思路:当一个事件被触发时,设置一个标志位,表示当前正在执行的回调函数,如果在执行过程中该事件又被触发,则忽略该次触发,直接回调函数执行完毕,在重置标志位。这样可以保证在一段时间内,只执行一次回调函数。节流的作用是限制事件的执行频率,比如滚动加载,拖拽等。
代码示例:
js
// 节流函数
function throttle(fn, delay) {
// 定义一个标志位
let flag = true;
// 返回一个新的函数
return function(…args) {
// 如果标志位为false,则直接返回
if (!flag) {
return;
}
// 设置标志位为false
flag = false;
// 设置一个延时函数
setTimeout(() => {
// 调用原始函数,并绑定this和参数
fn.apply(this, args);
// 重置标志位为true
flag = true;
}, delay);
};
}四、防抖 Debounce - 最后一次有效
基本思路:当一个事件被触发时,设置一个延时函数,如果在延时时间内,该事件又被触发,则取消之前的延时函数,重新设置一个新的延时函数,这样可以保证只有在事件停止触发一段时间后,才执行真正的回调函数。防抖的作用是避免频繁的执行一些消耗性能的操作,比如输入框搜索,窗口大小调整等。
代码示例:
js
// 防抖函数
function debounce(fn, delay) {
// 定义一个定时器
let timer = null;
// 返回一个新的函数
return function (...args) {
// 如果已经有定时器,则取消
if (timer) {
clearTimeout(timer);
}
// 设置一个新的定时器
timer = setTimeout(() => {
// 调用原始函数,并绑定this和参数
fn.apply(this, args);
}, delay);
};
}
export const debounce = (fn, delay) => {
let timer = null;
return function (...args) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};五、在 vue 中使用
记一个疑问,总以为使用的时候写箭头函数和封装方法也写箭头函数有关系,封装方法时候,写箭头函数和 function 都可以,只要使用的时候,按照下方使用就行
vue
<script setup>
import { debounce } from "文件路径";
const inputChange = debounce(() => {
// 业务逻辑
}, 2000);
</script>六、另一种写法
js
let timer = null;
export function debounce(fn, delay = 1000) {
if (timer != null) {
clearTimeout(timer);
timer = null;
}
timer = setTimeout(fn, delay);
}
let timer = null;
export function throttle(fn, delay = 300) {
if (timer == null) {
timer = setTimeout(() => {
fn();
clearTimeout(timer);
timer = null;
}, delay);
}
}vue
<script setup>
import { debounce, throttle } from "文件路径";
const inputChange = () =>
debounce(() => {
// 业务逻辑
}, 2000);
const inputChange = () =>
throttle(() => {
// 业务逻辑
}, 1000);
</script>七、新的封装,使用vue3中的customRef来实现封装
customRef:创建一个自定义的 ref,显式声明对其依赖追踪和更新触发的控制方式。
customRef()预期接收一个工厂函数,这个工厂函数接收track和trigger两个函数作为参数,并返回一个带有get()和set()方法的对象。
正常情况下,在get()里面调用track(),在set()中调用trigger()。
js
import { customRef, ref } from “vue”;
// 定义一个防抖函数
// data为初始化的的值
function debounceRef(data, delay = 300) {
// 创建一个定时器
let timer = null;
// 返回一个自定义的ref
// 对 delay 进行判断,如果传递的是 null 则不需要使用 防抖方案,直接返回使用 ref 创建的。
return delay == null ? ref(data) : customRef((track, trigger) => {
return {
get() {
// 收集依赖
track();
// 返回当前数据
return data;
},
set(value) {
// 清除定时器
if (timer) {
clearTimeout(timer);
timer = null
}
// 设置一个新的定时器
timer = setTimeout(() => {
// 更新数据
data = value;
// 触发更新
trigger();
}, delay);
},
};
});
}
// 节流函数
// data 为创建时的数据
// delay 为节流时间
function throttleRef (data, delay = 300){
// 创建定时器
let timer = null;
// 对 delay 进行判断,如果传递的是 null 则不需要使用 节流方案,直接返回使用 ref 创建的。
return delay == null
?
// 返回 ref 创建的
ref(data)
:
// customRef 中会返回两个函数参数。一个是:track 在获取数据时收集依赖的;一个是:trigger 在修改数据时进行通知派发更新的。
customRef((track, trigger) => {
return {
get () {
// 收集依赖
track()
// 返回当前数据的值
return data
},
set (value) {
// 判断
if(timer == null){
// 创建定时器
timer = setTimeout(() => {
// 修改数据
data = value;
// 派发更新
trigger()
// 清除定时器
clearTimeout(timer)
timer = null
}, delay)
}
}
}
})
}vue
<script setup>
// 创建
const count = debounceRef(0, 300)
// 函数中使用
const addCount = () => {
count.value += 1
}
// v-model 中使用
<input type="text" v-model="count">