什么是前端防抖和节流?它们的区别是什么?怎么使用的?
无
概念:触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间。
思路:每次触发事件的时候,清除定时器,然后从新设置新的定时器。
应用场景:
立即执行防抖vs非立即执行防抖
- 非立即执行防抖:指事件触发后,回调函数不会立即执行,会在延迟时间 n 秒后执行,如果 n 秒内被调用多次,则重新计时延迟时间。
- 立即执行防抖:指事件触发后,回调函数会立即执行,之后要想触发执行回调函数,需等待 n 秒延迟。
基本vue使用:
// 非立即执行防抖
onDebounce() {
// timer是该对象全局定义的,每次执行onDebounce函数,调用的相同的timer
this.timer && clearTimeout(this.timer);
this.timer = setTimeout(()=>{
this.getAjax(); // 后台请求ajax信息
}, 2000);
},
// 立即执行防抖
onDebounce() {
this.callNow = !this.timer;
// timer是该对象全局定义的,每次执行onDebounce函数,调用的相同的timer
this.timer && clearTimeout(this.timer);
this.timer = setTimeout(()=>{
this.timer = 0;
}, 2000);
this.callNow && this.getAjax(); // 后台请求ajax信息
},
基本js代码:
// 非立即执行防抖
function debounce(fn) {
let timeout = null; // 创建一个闭包,保存一个标记用来存放定时器的返回值
return function () {
timeout && clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.call(this);
}, 500);
};
}
document.getElementById('inp').addEventListener('input', debounce(getAjax)); // 防抖
// 立即执行防抖
function debounce(fn) {
let timeout = null; // 创建一个闭包,保存一个标记用来存放定时器的返回值
return function () {
let callNow = !timeout;
timeout && clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
timeout = 0;
}, 500);
callNow && fn.call(this);
};
}
document.getElementById('inp').addEventListener('input', debounce(getAjax)); // 防抖
概念:高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率。
简要思路:每此触发事件时判断是否定时器已满,满则重新定时,否则不做任何事。
具体思路1:设置一个触发标志flag=true,在事件里面判断:如果flag==true,进行一次定时任务,并将flag=false,在定时任务里面执行获取ajax的代码,并将flag=true;如果flag=false,直接return出去不做任何事情,直到定时器满。
具体思路2:不使用定时器,直接比较时间戳差值。
应用场景:
思路1:定时器模式-vue代码:
onThrottle() {
if(!this.throttleFlag) return;
this.throttleFlag = false;
this.timer = setTimeout(()=>{
this.getAjax();
this.throttleFlag = true;
}, 2000);
}
思路1:定时器模式-js代码:
function throttle(fn) {
let canRun = true; // 通过闭包保存一个标记
return function () {
if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
canRun = false; // 立即设置为false
setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
// 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
canRun = true;
}, 500);
};
}
window.addEventListener('resize', throttle(getAjax));
思路2:时间戳模式-vue代码:
onThrottle() {
let now = Date.now();
if (now - this.lastTime > 5000) { // 5s
this.getAjax();
this.lastTime = now; // update时间
}
}
思路2:时间戳模式-js代码:
function throttle (fn) {
let lastTime = 0; // 上次触发的时间
return function () {
let now = Date.now();
if (now - lastTime > 5000) {
fn.call(this);
lastTime = now; // update时间
}
}
}
(完)