高性能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; } }
事件委托并不难,你只需要检查事件是否来自你所预期的元素,然后根据预期的元素来执行相应元素所绑定的事件处理程序。