高性能JavaScript chapter 1 : 加载和执行
如何用最高效的方式把javascript代码加载到页面中呢?
1、脚本位置
每个文件必须等到前一个文件下载并执行完成才会开始下载。在这些文件逐个下载过程中,用户看到的是一片空白。称为脚本阻塞。因此推荐将所有的标签尽可能放到标签的底部,以尽量减少对整个页面下载的影响。
<html>
<head>
<title>script example</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<p>Hello world!</p>
<!-- 推荐的脚本存放位置 -->
<script type="text/javascript" src="file1.js"></script>
<script type="text/javascript" src="file2.js"></script>
<script type="text/javascript" src="file1.js"></script>
</body>
</html>
2、组织脚本
(1)浏览器在解析HTML页面的过程中,每遇到一个<script>标签,都会因执行脚本而导致一定的延时,所以减少页面所包含的<script>标签数量有助于改善页面性能。
(2)HTTP请求会带来额外的性能开销,因此下载单个100KB的文件将比下载4个25KB的文件更快。即,减少页面中外链脚本文件的数量将会改善性能。
综述:用文件打包工具把多个文件合并成1个,这样只需要引用一个<script>标签,就可以减少性能消耗。
<html>
<head>
<title>script example</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<p>Hello world!</p>
<!-- 推荐的脚本存放位置 -->
<script type="text/javascript" src="file1.js"></script>
</body>
</html>
3、无阻塞的脚本
秘诀:在页面加载完成后才加载JavaScript代码。
(1)延迟的脚本
带有defer属性的JavaScript文件将在页面解析到<script>标签时开始下载,但并不会执行,下载时,不会阻塞浏览器的其他进程,可以与页面中其他资源并行下载。它下载完成后,在onload事件被触发前执行。
该属性只有IE4+ 和 firefox3.5+ 的浏览器支持。
<head>
<title>script defer example</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<script defer>
alert("defer");
</script>
<script>
alert("script");
</script>
<script>
window.onload = function(){
alert("load");
}
</script>
</body>
</html>
<small>该 示例 在支持defer属性的浏览器上,弹出的顺序是:script , defer , load.</small>
(2)动态脚本文件
该脚本文件被添加到页面时开始下载,无论在何时启动下载,文件的下载和执行过程不会阻塞页面其他进程。
<html>
<head>
<title>动态 script example</title>
</head>
<body>
<script>
function loadScript( url , callback ){
var script = document.createElement("script");
script.type = "text/javascript";
if( script.readyState ){ // IE
script.onreadystatechange = function(){
if( script.readyState == "loaded" || script.readyState == "complete" ){
script.onreadystatechange = null;
callback();
}
}
} else { // 其他浏览器
script.onload = function(){
callback();
}
}
script.src = url ;
document.getElementByTagName("head")[0].appendChild(script);
}
loadScript("file1.js" , function(){
alert("file is loaded !");
})
</script>
</body>
</html>
<small>该 示例 用到<script>的readyState属性,该属性有5种取值:
uninitialized 初始状态
loading 开始下载
loaded 下载完成
interactive 数据完成下载但尚不可使用
complete 所有数据已准备就绪</small>
(3)XMLHttpRequest(xhr对象) 脚本注入
优点1:你可以把脚本的执行推迟到你准备好的时候。
优点2:在所有主流浏览器都能正常使用。
缺点:JavaScript文件必须与所请求的页面属于相同的域。
<html>
<head>
<title>xhr script example</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.open("get" , "file1.js" , true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304 ){
var script = document.createElement("script");
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
}
xhr.send(null);
</script>
</body>
</html>
<small>这段代码发送一个get请求获取file1.js文件。事件处理函数onReadyStateChange 检查readyState 是否为4 ,同时校验HTTP状态码是否有效(2XX 表示有效响应,304意味着是从缓存读取)。如果收到了有效响应,就会创建一个<script>元素,设置该元素的text属性为从服务器接收到的responseText。</small>
(4)作者推荐的无阻碍模式
- 第一种:YUI3的方式
YUI3 API:https://github.com/yui/yui3
设计理念:由页面中的少量代码来加载丰富的功能组件。
<html>
<head>
<title>YUI3 example</title>
</head>
<body>
<script src="http://yui.yahooapis.com/3.18.1/build/yui/yui-min.js"></script>
<script>
// Create a YUI sandbox on your page.
YUI().use('node', 'event', function (Y) {
// The Node and Event modules are loaded and ready to use.
// Your code goes here!
});
</script>
</body>
</html>
- 第二种:LazyLoad类库
LazyLoad源文件代码:http://github.com/rgrove/lazyload/
<html>
<head>
<title>LazyLoad example</title>
</head>
<body>
<script type="text/javascript" src="lazyload.js"></script>
<script>
LazyLoad.js("the-rest.js",function(){
Application.init();
});
</script>
</body>
</html>
- 第三种:LABjs
LazyLoad源文件代码:https://github.com/getify/LABjs
特点:通过wait()管理依赖关系
<html>
<head>
<title>LABjs example</title>
</head>
<body>
<script type="text/javascript" src="LAB.js"></script>
<script>
$LAB.script("first-file.js").wait()
.script("the-rest.js")
.wait(function(){
Application.init();
});
</script>
</body>
</html>
<small>在前面的例子中,不能保证first-file.js的代码在the-rest.js的代码前执行。
为了确保这一点,你必须在第一个script()方法后调用wait(),比如,
如下示例可以保证first-file.js的代码在the-rest.js的代码前执行。</small>
<html>
<head>
<title>LABjs example</title>
</head>
<body>
<script type="text/javascript" src="LAB.js"></script>
<script>
$LAB.script("first-file.js").wait()
.script("the-rest.js")
.wait(function(){
Application.init();
});
</script>
</body>
</html>