高性能javascript

1、加载与执行

  由于多数浏览器使用单一线程来处理用户UI刷新和javascript脚本执行,所以同一时刻只能做一件事。这样就告诉我们js执行时间过程越久,浏览器等待响应的时间就越长,这里就存在一个性能问题,就是脚本的位置问题。

由于脚本会阻塞页面渲染,所以只有js全部下载并且执行完成后才会渲染页面,那么我们期望js不要在页面渲染的时候进行加载。因此我们推荐的方式是:

所有script标签尽可能的放到body标签的底部,以尽量减少对整个页面下载的影响。

1、动态脚本元素

var script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function(){//非IE
    alert('script loaded');
};
script.onreadystatechange = function(){//IE
    if(script.readyState == 'loaded' || script.readyState == 'complete'){
        script.onreadystatechange = null;
        alert('script loaded');
    }
};
script.src = 'file1.js';
document.getElementsByTagName('head')[0].appendChild(script);
onreadystatechange 有五个值:如下
uninitialized
初始状态
loading
开始下载
loaded
下载完成
interactive
数据完成下载但尚不可用
complete
所有数据已准备就绪
动态加载脚本的兼容性写法


function loadScript(url,callback){
    var script = document.createElement('script');
    script.type = 'text/javascript';
    if(script.readyState){//IE
        if(script.readyState == 'loaded' || script.readyState == 'complete'){
            script.onreadystatechange = null;
            callback();
        }
    }else{
        script.onload = function(){//非IE
            callback();
        };
    }
    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
}

使用方法如下:

loadScript('file1.js',function(){
    alert('file is loaded!');
});

loadScript('file1.js',function(){
    loadScript('file2.js',function(){
        loadScript('file3.js',function(){
            alert('all files are loaded!');
        });
    });
});

XMLHttpRequest 脚本注入

var xhr = new XMLHttpRequest();
xhr.open('get','file1.js',true);
xhr.onreadystatechange = function(){
    if(readyState == 4){
        if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = xhr.responseText;
            document.body.appendChild(script);
        }
    }
}

通过get请求一个js文件。 http状态码 2xx表示有效响应,304表示从缓存读取

缺点是该代码不支持跨域,针对走cdn的文件不能采用这个方法

动态加载js的通用工具可以在下面地址获取

https://github.com/rgrove/lazyload/

使用方法如下:

LazyLoad.js('file1.js',function(){
    alert('file is loaded');
});

//按顺序加载多个文件
LazyLoad.js(['1.js','2.js'],function(){
    alert('file is loaded');
});

第二种

LABjs

http://labjs.com/

用法举例

$LAB.script('file.js').wait(function(){
    //初始化
});
//lab script方法用来下载文件,wait方法用来指定文件下载并执行完毕后所调用的函数。支持链式调用
$LAB.script('1.js')
    .script('2.js')
    .wait(function(){
        //初始化
    });

前面的举例中1.js不一定会保证在2.js前调用。要保证这一点可以这么写

$LAB.script('1.js').wait()
    .script('2.js')
    .wait(function(){
        //初始化
    });

以上都是比较好的动态加载js的方式,且不会阻塞浏览器!

例子和说明参考来自《高性能javascript》第一章

事件委托

当页面中存在大量元素的时候,要为每个元素绑定一个或多个事件处理程序,这种情况可能会影响性能!每绑定一次事件处理程序都是有代价的,它加重了页面的负担,增加了运行期间的执行时间。

那么一个好的事件处理程序的技术是事件委托。它基于这样一个事实:事件是逐层向上冒泡并能被父元素捕获。使用事件代理只需给父元素绑定一个事件处理程序,就可以处理其子元素上触发的所有的事件。

在IE下不支持捕获,但是只要支持冒泡就足够了!

例如下面的例子:

document.getElementById('main').onclick = function(e){
    e = e || window.event;
    var target = e.target || e.srcElement;
    var pageId,hrefparts;
    if(target.nodeName !== 'A'){
        return ;
    }
    hrefparts = target.href.split('/');
    pageId = hrefparts[hrefparts.length-1];
    pageId = pageId.replace('.html','');
    var xhr = new XMLHttpRequest();
    xhr.open('get','page.php?page='+pageId,true);
    xhr.onreadystatechange = function(){
        if(readyState == 4){
            if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
                //更新页面
            }
        }
    }
    if(typeof e.preventDefault === 'function'){
        e.prenentDefault();
        e.stopPropagation();
    }else{
        e.returnValue = false;
        e.canceBubble = true;
    }
}

事件委托并不难,你只需要检查事件是否来自你所预期的元素,然后根据预期的元素来执行相应元素所绑定的事件处理程序。