防抖:不断触发一个方法,在规定时间内只让最后一次有效访问,如果这期间又再次触发,则重新计时
应用场景:
1. 登录、发短信、搜索框输入等按钮避免用户点击太快,以致于发送了多次请求,需要防抖。
2. 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖。
3. 文本编辑器实时保存,当无任何更改操作一秒后进行保存。
代码实现要点:设置一个定时器,通过闭包,抓住定时器变量,控制定时器的添加和删除
vue3写法
<el-input
v-model="str"
placeholder="请输入"
@keyup="testDebounce"
style="width: 300px"
clearable
/>
<script>
const testDebounce=debounce((e)=>{
console.log(e.target.value)
},2000)
//防抖实现
function debounce(fn,delay){ //传入所要防抖的方法或者回调与延迟时间
let timer = null
//借助闭包,使得变量timer不被回收
// 将debounce处理结果当作函数返回
return function() {
//保存当前环境下的实例对象,this即为引入该方法的那个组件实例对象
let th = this;
//保存传入参数
let args = arguments;
//第一次timer为null,跳过该判断,执行setTimeout()
if(timer){
//之后如果timer存在,则把上一次的销毁,也就是setTimeout(),则里面的函数执行也会抛弃
clearTimeout(timer)
}
timer = setTimeout(()=>{
//apply(),改变this指向,指向正在操作的组件实例,传入参数
fn.apply(th, args)
}, delay)
}
}
</script>
//由于使用闭包,使得timer不被回收,在A组件中每次调用该方法时会去判断timer是否存在,如果存在,
//表示上一次输入在等待执行fn(),期间如果继续输入,且在1s内,则会把上一次未执行的
//(setTimeout中的)fn()销毁,重新定时1s,以此来达到输入结束过1s才执行fn(),即触发事件或者发送
//请求。
vue2写法
<template>
</template>
<script>
/* 防抖函数 */
let debounce = function (callback, delay) {
// 使用闭包的外部变量来定义定时器
let timer;
return function () {
// 判断是否已经存在定时任务
if (timer) {
/*如果已有定时任务就将已有任务清除,再重新生成新的定时任务*/
clearTimeout(timer)
}
// 生成定时任务并赋值给timer
timer = setTimeout(() => {
callback.call(this)
}, delay)
}
}
let ajax = function () {
//利用定时器来模拟异步操作
setTimeout(() => {
// 使用日志输出来模拟ajax请求
console.log("发起ajax请求" + ",时间戳为:" + new Date());
}, 1000)
}
// 给输入框对象绑定键盘输入事件
input.oninput = debounce(ajax, 1000)
</script>
节流:单位时间内,只能触发一次(首次)
用场景:
1. 滚动scroll 事件,每隔一秒计算一次位置信息。
vue3写法
<template>
<div style="width: 100%; height: 2000px; background-color: pink">滚动事件</div>
</template>
<script>
const throttle = (fn, delay = 2000) => {
let flag = true
return function() {
let th = this;
let args = arguments;
if(!flag){
//未超过时间间隔,flag无效,不执行fn
return false
}
fn.apply(th, args)
flag = false //在时间间隔内把状态位flag设为无效(false)
setTimeout(() => {
flag = true //超过时间间隔把状态位flag设为有效(true)
}, delay)
}
};
const better_scroll = throttle(e => {
console.log('触发了滚动事件' + e.target.documentElement.scrollTop);
},3000);
onMounted(() => {
document.addEventListener('scroll', better_scroll);
});
export function _throttle(fn, interval) {
var last;
var timer;
var interval = interval || 200;
return function () {
var th = this;
var args = arguments;
var now = +new Date();
if (last && now - last < interval) {
clearTimeout(timer);
timer = setTimeout(function () {
last = now;
fn.apply(th, args);
}, interval);
} else {
last = now;
fn.apply(th, args);
}
}
}
</script>
vue2写法
<template>
</template>
<script>
/* 通过判断flag来实现节流 */
let throttle = function (callback, delay) {
// 判断依据
let flag = true
return function () {
// 如果flag为false便忽略这次操作
if (flag) {
//设定定时器,当任务执行时将flag恢复false,允许下一次的事件触发
setTimeout(() => {
callback.call(this)
flag = true
}, delay)
}
//在定时任务执行之前,flag始终为false
flag = false
}
}
/* 通过时间来判断 */
let throttling = function (callback, delay) {
// 设置一个记录的时间,用以判断是否忽略操作
let time = 0;
return function () {
// 创建当前时间,用以判断是否超过设定好的延迟
let now = new Date()
// 如果两次事件触发时间差大于设定好的毫秒数,则触发新的请求
if (now - time > delay) {
// 执行回调函数
callback.call(this)
// 将记录的时间设置为这一次执行任务的时间
time = now
}
}
}
let ajax = function () {
//利用定时器来模拟异步操作
setTimeout(() => {
// 使用日志输出来模拟ajax请求
console.log("发起ajax请求");
}, 1000)
}
window.onscroll = throttling(ajax, 3000)
</script>
总结
防抖主要是为了解决事件频繁触发的问题,且仅采取频繁触发的最后一次操作。
节流也是为了解决事件频繁触发的问题,且仅采取频繁触发的第一次操作。