前端性能优化(一)
性能优化-函数防抖
当页面的元素被拖拽,触发了mousemave事件,或页面滚动scroll事件,或输入框keyup事件等,事件频繁触发,如果事件修改了dom节点、资源加载等行为,将会导致页面重绘次数过多,性能下降。针对频繁触发的事件,我们要通过函数防抖去做性能的优化。
函数防抖(debounce)
所谓的函数防抖,就是让函数在上一次执行之后,满足等待的某个时间内不再触发此函数后再执行,而在这个等待时间内再次触发此函数,则等待时间将会重新计算,即当函数触发n毫秒后,才会再一次执行该动作,若在这n毫秒内又调动此动作则将会重新计算执行时间。
- 场景
任务需求: 监听编辑器的内容,实时生成关键词。
很明显,拿到这个需求的第一反应就是用到了oninput实时监听编辑器的内容,一旦有内容的改变,那么就触发一个请求,发送数据到服务器的接口,然后通过分词,获取到这段内容的关键词。
然而,每一次内容的改变都要触发一次请求,这对服务器的压力很大,所以要用的函数防抖。
- 原理
所谓的函数防抖,其实很简单,就是通过定时器,管理这个函数是否执行。这是一个简单的例子
var timer = null;
function debounce () {
clearTimeout(timer);
timer = setTimeout(function () {
console.log(1)
}, 3000);
}
利用定时器,让函数延迟3秒后执行,若在这3秒内,如果这个函数又一次的被调用,则删除上一次的函数调用,重新计算时间,3秒后执行。
- 案例
再换一个案例,这一把是触发scroll事件进行演示。
// 没有函数防抖的情况
window.onscroll = function () {
say();
};
function say () {
console.log('哈哈 触发了scroll事件');
}
经过测试我们很明显发现控制台打印了很多”哈哈 触发了scroll事件”这句话。而我们只想触发一次就好,这里我们就要做一个简单的函数防抖了。
// 有函数防抖的情况
window.onscroll = function () {
say();
};
var timer = null;
function say () {
clearTimeout(timer);
timer = setTimeout(function () {
console.log('哈哈 触发了scroll事件');
}, 1000);
}
经过则是我们同样很明显发现,只有在滚动停止1秒后,控制台打印了一句”哈哈 触发了scroll事件”,所以没有了那么多的请求和渲染。
// 提取方法
window.onscroll = function () {
debounce(say, window);
};
function say () {
console.log('哈哈 触发了scroll事件');
}
function debounce (method, context) {
clearTimeout(method.timer);
method.timer = setTimeout(function () {
method.call(context);
}, 1000);
}
经测试,与一个例子效果相同,我们只是把方法提取出来,方便多次使用。
- 第三方封装
underscore.js
的函数防抖定义: ` _.debounce(fn, wait, [immediate])`;
/**
* debounce 函数防抖
* method 需要进行函数防抖的方法
* delay 需要延迟的时间
* immediate 是否需要立即执行初始事件 默认false
*/
_.debounce = function(fn, wait, immediate) {
var timeout,
args,
context,
timestamp,
result;
var later = function() {
var last = _.now() - timestamp;
if(last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if(!immediate) {
result = fn.apply(context, args);
if(!timeout) {
context = args = null;
}
}
}
};
return function() {
context = this;
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
if(!timeout) {
timeout = setTimeout(later, wait);
}
if(callNow) {
result = fn.apply(context, args);
context = args = null;
}
return result;
}
};
使用方式
$('#input').keypress(_.debounce(function () {
// 异步调用查询
console.log('调用了');
}, 300));
性能优化-函数节流
面对mousemove事件、resize事件等,在监听这类事件的时候,一般都会短时间频繁重复触发,如果伴随着DOM节点的操作,那么对于浏览器性能压力很大。
函数节流(throttle)
函数节流是针对频繁触发的事件进行一个限制,让这些函数在每隔一段时间或每次满足一定条件下进行触发。
- 原理
通过一个定时器阻断连续重复函数的调用
- 示例
// 正常情况
<div id="container"></div>
document.getElementById("container").onmousemove = alertSomething;
function alertSomething () {
console.log('哈哈~ 我被触发了');
}
上面这个代码段就是,给这个div绑定了一个鼠标移动事件,鼠标在这个div里移动时就会在控制台打印一句话。如果我们没有做任何限制,那么他会不断的处罚这个事件。
// 加入函数节流
function throttle (method) {
var timer = null;
var lastTime = 0;
return function () {
var context = this;
var currTime = new Date().getTime();
var args = arguments;
clearTimeout(timer);
if (!lastTime) {
lastTime = currTime;
}
// 阀值1000ms
if (currTime - lastTime >= 1000) {
method.apply(context, args);
lastTime = currTime;
} else {
timer = setTimeout(function () {
method.apply(context, args);
}, 1000);
}
};
}
document.getElementById("container").onmousemove = throttle(alertSomething);
function alertSomething () {
console.log('哈哈~ 我被触发了');
}
如此简单的封装了一个throttle
方法,我们实现了一个简单的函数函数节流.