本文是阅读《高性能JavaScript》一书后,从 加载和执行 模块对JavaScript性能优化做了部分总结,记录一下。可能总结的不好,不是很完整,也希望各位大佬能多给出一些建议。万分感谢!
多数浏览器使用单一进城来处理用户界面(UI)刷新和JavaScript脚本执行,所以同一时刻只能做一件事。JavaScript执行过程耗时越久,浏览器等待相应的时间就越长。
简单说,这意味着<script>
标签每次出现都霸道地让页面等待脚本的解析和执行,无论当前的JavaScript代码是内嵌还是包含在外链文件中,页面的下载和选人都必须听下来等待脚本执行完成。
脚本位置
- 将所有的
<script>
标签尽可能的放到<body>
标签的底部(雅虎特别性能小组提出的优化JavaScript的首要规则)
组织脚本
浏览器在解析HTML页面的过程中每遇到一个
<script>
,都会因执行脚本而导致一定的演示,因此最小化延时时间会名校改善页面的总体性能建议永远不要把内嵌脚本紧跟在
<link>
标签后面,因为将一段内嵌脚本放在引用外联样式表的<link>
标签之后会导致页面阻塞去等待样式表的下载。这么做是为了确保内嵌脚本在执行时能获得最精准的样式信息减少页面中外链脚本的数量,将多个文件合并成一个
无阻塞的脚本
意味着页面加载完成之后才加载JavaScript代码
- 给
<script>
标签添加 async 属性,则资源下载完成自动执行
延迟的脚本
给
<script>
标签添加 async 属性,则资源下载完成也必须等到页面加载完成后执行PS:async和defer的共同点和区别:
共同点:采用并行下载,在下载过程中不会产生阻塞
区别:执行时机不同,async是加载完成后自动执行,而defer需要等待页面完成后执行
注意: 使用defer和async时最好先查阅当前浏览器是否支持该属性
动态脚本元素
使用动态代码操作DOM创建<script>
元素,这种技术的重点在于:无论何时启动下载,文件的下载和执行过程不会阻塞页面其他进行。设置可以将代码放在<head>
区域而不会影响页面其他部分
注意点
创建的
<script>
标签添加到<head>
标签里比添加到<body>
里更保险- 因为当
<body>
中的内容没有全部加载完成时,IE可能会抛出一个“操作已终止”的错误信息
- 因为当
当脚本“自执行”时,这种机制运行正常。但是当代码只包含供页面其他接口调用的接口时,就会有问题
在这种情况下,必须跟踪并确保脚本下载完成并准备就绪
实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function loadScript(url, callback) {
var script = document.createElement('script);
script.type = "text/script";
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.getElementsByTagName("head")[0].appendChild(script);
}调用方法
1
2
3
4
5
6
7loadScript("file1.js", function() {
loadScript("file2.js", function() {
loadScript("file2.js", function() {
alert("所以文件加载完成");
})
})
})
XMLHTTPRequest脚本注入
此技术会先创建一个XHR对象,然后用它下载JavaScript文件,最后通过动态
<script>
元素将代码注入到页面中1
2
3
4
5
6
7
8
9
10
11
12var xhr = new XMLHttpRequest();
xhr.open("get", "file1.js", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.statue < 300 || xhr.status == 304) {
var script = document.createElement("script");
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
}优点:可以下载JavaScript代码但不立即执行,由于代码实在标签之外返回的,因此下载后不会自动执行,可以将脚本的执行推迟到页面准备好的时候
局限性:JavaScript代码必须与所请求的页面处于相同的域,这意味着JavaScript文件不能从CDN下载
推荐的无阻塞模式
向页面中添加大量JavaScript的推荐方式只需两步:先动态加载所需的代码,然后加载初始化页面所需的剩下的代码。第一部分的代码尽量精简
第一种做法
1
2
3
4
5
6<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript">
loadScript("the-rest.js", function() {
Application.init();
})
</script>第二种做法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<script type="text/javascript">
function loadScript(url, callback) {
var script = document.createElement('script);
script.type = "text/script";
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.getElementsByTagName("head")[0].appendChild(script);
}
loadScript("the-rest.js", function() {
Application.init();
})
</script>
第二种做法可以比多产生一次HTTP请求,但是建议使用YUI Compressor 把初始化代码压缩到最小尺寸
YUI3 的方式
YUI3核心设计理念:由页面中的少量代码来加载丰富的功能组件
使用
1
2
3
4
5
6
7
8<script type="text/javascript" src="http://yui.yahooapis.com/combo?3.0.0/build/yui/yui-min.js"></script>
<script type="text/javascript">
// dom实际上拼装出一个带有所有依赖文件组合的URL
// Y,当前YUI实例
YUI.use("dom", function(Y) {
Y.DOM.addClass(document.body, "loaded")
})
</script>
LazyLoad 类库
- 使用
1
2
3
4
5
6
7<!-- LazyLoader 源代码:http://github.com/rgrove/lazyload -->
<script type="text/javascript" src="lazyload-min.js"></script>
<script>
LazyLoad.js(["first-file.js", "the-rest.js"], function() {
Application.init()
})
</script>
LABjs
- 使用
1
2
3
4
5
6
7
8
9
10
11<!-- LABjs源代码:http://labjs.com -->
<!-- 虽然是同归.script链式调用,其实也是并行下载 -->
<script type="text/javascript" src="lab.js"></script>
<script>
// wait 意味着加载好前面的javascript资源才会继续向下加载
$LAB.script("first-file.js").wait()
.script("the-rest.js")
.wait(function () {
Appication.init();
})
</script>
小结
减少JavaScript对性能的影响的方式:
</body>
闭合标签之前,将所有的<script>
放到页面底部。这能确保在脚本执行前页面已经完成了渲染合并脚本。页面中的
<script>
标签越少,加载也就越快,响应也更迅速,无论外链文件还是内嵌脚本都是如此有多种无阻塞下载JavaScript的方法:
使用
<script>
标签的defer属性使用动态创建的
<script>
元素来下载并执行代码使用XHR对象下载JavaScript代码并注入到页面中