防抖
与 节流
是前端在优化性能问题上,经常使用的两种技术手段。比如 input
,scroll
,resize
,mousemove
等事件,如果不加以控制,频繁的触发,无疑将会带来额外的性能开销,极端情况下,可能造成死机卡死现象。今天,我们我一起来聊聊他们吧。
默认情况
在讨论防抖和节流之前,我们先来看看,在不做任何处理情况下,一个事件频繁触发,会是怎样的一种情况。
假如有如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#content {
width: 200px;
padding: 50px;
color: #fff;
font-size: 30px;
text-align: center;
background-color: #8597a3;
}
</style>
</head>
<body>
<div id="content">0</div>
<script></script>
</body>
</html>
有如下 js:
var count = 0;
var el = document.getElementById('content');
function handle() {
el.innerHTML = ++count;
}
el.onmousemove = handle;
当鼠标在元素 div 中移动的时候,效果如下:
event 事件触发 与 handle 事件的执行,关系如下:
防抖
防抖,也叫去抖动,就是当事件快速连续不断触发时,动作只会执行一次。
也可以这么理解:就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
防抖函数分为,延迟防抖
和 前缘防抖
,区别就是是否会立即执行。
延迟防抖
var count = 0;
var el = document.getElementById('content');
var debounce = function(fn, delay) {
var timer = null;
return function() {
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(fn, delay)
}
};
function handle() {
el.innerHTML = ++count;
}
el.onmousemove = debounce(handle, 300);
效果如下:
event 事件触发 与 handle 事件的执行,关系如下:
event 事件触发时,延迟一定时间触发 handle 事件。
上面代码,有个问题,就是 handle 函数,接收不到 event 对象,所以,需要改进下:
var count = 0;
var el = document.getElementById('content');
var debounce = function(fn, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
fn.apply(context, args)
}, delay)
}
};
function handle(e) {
console.log(e);
el.innerHTML = ++count;
}
el.onmousemove = debounce(handle, 300);
前缘防抖
var count = 0;
var el = document.getElementById('content');
var debounce = function(fn, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if(timer) {
clearTimeout(timer)
}
if(!timer) {
fn.apply(context, args)
}
timer = setTimeout(function() {
timer = null;
}, delay)
}
};
function handle(e) {
console.log(e);
el.innerHTML = ++count;
}
el.onmousemove = debounce(handle, 300);
效果如下:
event 事件触发 与 handle 事件的执行,关系如下:
节流
节流,就是当事件快速连续不断触发时,动作会按照一定时间规律执行。
节流有两种写法,时间戳写法 和 定时器写法。
时间戳写法
var count = 0;
var el = document.getElementById('content');
var throttle = function(fn, delay) {
var begin = 0
return function() {
var context = this;
var args = arguments;
var now = Date.now();
var timespend = now - begin;
if(timespend >= delay) {
fn.apply(context, arguments)
begin = now;
}
};
}
function handle(e) {
console.log(e);
el.innerHTML = ++count;
}
el.onmousemove = throttle(handle, 1000);
效果如下:
event 事件触发 与 handle 事件的执行,关系如下:
注意上图,开头 handle 事件,event 事件刚触发,handle 事件就执行了。也就是说,时间戳,实现的节流,handle 事件是在给定时间段的开头执行。
结尾浅色 handle 事件不执行,原因是,如果给定的间隔时间是1s,而只连续触发了 0.5s ,没达到指定间隔,因此不执行。
下面再来看定时器写法。
定时器写法
var count = 0;
var el = document.getElementById('content');
var throttle = function(fn, delay) {
var timer = null;
return function() {
var context = this;
var args = arguments;
if(!timer) {
timer = setTimeout(function() {
timer = null;
fn.apply(context, args);
}, delay);
}
};
}
function handle(e) {
console.log(e);
el.innerHTML = ++count;
}
el.onmousemove = throttle(handle, 1000);
效果如下:
event 事件触发 与 handle 事件的执行,关系如下:
从动图上可以看到,第 7 秒的时候,鼠标已经离开,过一会,仍然会跳到 8 。说明,定时器,实现的节流,handle 事件是在给定时间段的结尾执行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。