前端性能优化(一)

前端性能优化(一)

性能优化-函数防抖

当页面的元素被拖拽,触发了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方法,我们实现了一个简单的函数函数节流.

Done! By suki,引用请注明出处作者。
License CC BY-NC-SA 3.0.

0%