浏览器的 resize、scroll、keypress、mousemove 等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能。为了优化体验,需要对这类事件进行调用次数的限制。
在某一次高频触发下,我们只识别一次(可以控制开始触发,还是最后一次触发)
详细:假设我们规定500MS触发多次算是高频,只要我们检测到是高频触发了,则在本次频繁操作下(哪怕你操作了10次)也是只触发一次…
function () {
console.log("hello world")
}
上面按钮每一次点击,都会触发打印,如果疯狂点击,那么下面就会疯狂打印,会降低性能。
实际开发1:使用按钮只会,移除事件绑定
function handle() {
submit.onclick = null;
submit.disabled = true;
console.log('OK');
setTimeout(() => {
submit.onclick = handle;
submit.disabled = false;
}, 1000);
}
submit.onclick = handle;
实际开发2:使用按钮只会,移除事件绑定
let flag = false;
submit.onclick = function () {
if (flag) return;
flag = true;
console.log('OK');
setTimeout(() => {
// 事情处理完
flag = false;
}, 1000);
};
封装一个函数防抖的方法
function debounce(func, wait, immediate) {
// 多个参数及传递默认的处理
if (typeof func !== "function") throw new TypeError("func must be an function!");
if (typeof wait === "undefined") wait = 500;
if (typeof wait === "boolean") {
immediate = wait;
wait = 500;
}
if (typeof immediate !== "boolean") immediate = false;
// 设定定时器返回值标识
let timer = null;
return function proxy(...params) {
let self = this,
now = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(function () {
timer = null;
!immediate ? func.call(self, ...params) : null;
}, wait);
// 第一次触发就立即执行
now ? func.call(self, ...params) : null;
};
}
function handle(ev) {
// 具体在点击的时候要处理的业务
console.log('OK', this, ev);
}
submit.onclick = debounce(handle, true);
// submit.onclick = proxy; 疯狂点击的情况下,proxy会被疯狂执行,我们需要在proxy中根据频率管控handle的执行次数
// submit.onclick = handle; //handle->this:submit 传递一个事件对象
简洁版本的防抖
function myDebounce(fn, interval = 500) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arguments);
}, interval);
};
}
function doSomething(){
// onmousemove触发的事件回调函数
}
container.onmousemove = myDebounce(doSomething, 300);
在某一次高频触发下,我们不是只识别一次,按照我们设定的间隔时间(自己规定的频率),没到达这个频率都会触发一次
详细:假设我们规定频率是500MS,我们操作了10min,触发的次数=(10601000)/500
比如,滚动页面,触发了多次打印
body {
height: 3000px;
overflow-x: hidden;
background: -webkit-linear-gradient(top left, lightblue, pink, orange);
}
window.onscroll = function () {
// 默认情况下,页面滚动中:浏览器在最快的反应时间内(4~6MS),就会识别监听一次事件触发,把绑定的方法执行,这样导致方法执行的次数过多,造成不必要的资源浪费
console.log('OK');
};
封装一个函数节流的方法
body {
height: 3000px;
overflow-x: hidden;
background: -webkit-linear-gradient(top left, lightblue, pink, orange);
}
function throttle(func, wait) {
if (typeof func !== "function") throw new TypeError("func must be an function!");
if (typeof wait === "undefined") wait = 500;
let timer = null,
previous = 0; //记录上一次操作的时间
return function proxy(...params) {
let self = this,
now = new Date(), //当前这次触发操作的时间
remaining = wait - (now - previous);
if (remaining <= 0) {
// 两次间隔时间超过wait了,直接执行即可
clearTimeout(timer);
timer = null;
previous = now;
func.call(self, ...params);
} else if (!timer) {
// 两次触发的间隔时间没有超过wait,则设置定时器,让其等待remaining这么久之后执行一次「前提:没有设置过定时器」
timer = setTimeout(function () {
clearTimeout(timer);
timer = null;
previous = new Date();
func.call(self, ...params);
}, remaining);
}
};
}
function handle() {
console.log('OK');
}
window.onscroll = throttle(handle, 500);
简洁版本的节流
function myThrottle(fn, interval = 500) {
let run = true;
return function () {
if (!run) return;
run = false;
setTimeout(() => {
fn.apply(this, arguments);
run = true;
}, interval);
};
}
function doSomething(){
// onmousemove触发的事件回调函数
}
container.onmousemove = myThrottle(doSomething, 300);