编写高性能的JavaScript 脚本的加载与执行


Posted in Javascript onApril 19, 2010

脚本可以放在html页面的head里面,也可以放在body里面。
把脚本放在body中,当浏览器遇见<script>标签时, 浏览器不知道脚本会插入文本还是html标签,因此浏览器会停止分析html页面而去执行脚本。当使用src的方式添加脚本时,浏览器也会做同样的动作。在脚本处理的时候,页面呈现和用户交互将被完全阻止。脚本下载和执行阻塞了其他资源的下载,比如呈现页面使用的图片。(虽然很多浏览器实现了脚本并行下载的技术,但是这个问题依然没有解决)
脚本的位置
鉴于上面的理由,脚本应该始终放在页面的底部,即</body>前面。
一个简单的示例:

<html> 
<head> 
<title>Script Example</title> 
<link rel="stylesheet" type="text/css" href="styles.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="file3.js"></script> 
</body> 
</html>

合并脚本
因为脚本下载阻塞了页面呈现,因而应该减少页面<script>标签的使用,不管脚本是内联的还是外部的。在处理外部脚本的时候情况比较特殊,浏览器下载一个100kb的脚本的时间将远远小于4个25kb的脚本,因为建立一个请求要消耗大量的时间。所以页面应该尽量的减少外部脚本的引用。
非阻塞的脚本
秘诀在于当页面loading完成之后再来加载脚本,也就是在window对象的onload事件触发之前 。下面是实现的几种方式:
1.使用defer
<html> 
<head> 
<title>Script Defer Example</title> 
</head> 
<body> 
<script defer> 
alert("defer"); 
</script> 
<script> 
alert("script"); 
</script> 
<script> 
window.onload = function(){ 
alert("load"); 
}; 
</script> 
</body> 
</html>

页面弹出框出现的顺序: script/defer/load,这个技术的缺点是IE4+和FF3.5+才支持。
非阻塞的脚本(续)
2. 动态脚本元素
要知道<script>和普通的html标签并没有本质的区别,所以可以利用标准的DOM方法动态的添加脚本文件引用。方法如下:
var script = document.createElement("script"); 
script.type = "text/javascript"; 
script.src = "file1.js"; 
document.getElementsByTagName("head")[0].appendChild(script);

当这个标签一旦加入到html中,脚本文件就开始下载。这种方法的一个特点就是,文件下载和执行并不阻塞html页面其它部分的处理。通常将这样的脚本放在<head>中较之<body>更加安全,尤其是文件包含的代码需要在页面的load事件中执行。如果body的内容还没有被完全的加载,IE还会弹出“禁止操作”的错误。
当脚本文件下载完成之后,脚本立即执行(FF、Opera会等待前一个以同样方式添加的脚本执行)。当脚本自执行时,这没什么问题。但是如果脚本包含页面中其它脚本使用的interfaces,你需要确认脚本已经加载完成并且可用。幸好,当获得script标签的src的值之后,Firefox, Opera, Chrome, and Safari 3+ 会触发一个load事件。
var script = document.createElement("script") 
script.type = "text/javascript"; 
//Firefox, Opera, Chrome, Safari 3+ 
script.onload = function(){ 
alert("Script loaded!"); 
}; 
script.src = "file1.js"; 
document.getElementsByTagName("head")[0].appendChild(script);

IE则提供了另外一种解决方案--readystatechange事件。根据下载
脚本文件所处的状态,readyState 的值有以下几种:
"uninitialized"
默认状态
"loading"
开始下载
"loaded"
下载完成
"interactive"
下载完成,但是并非全部可用
"complete"
所有数据可用
IE的实现方式:
var script = document.createElement("script") 
script.type = "text/javascript"; 
script.onreadystatechange = function(){ 
if (script.readyState == "loaded" || script.readyState == "complete"){ 
script.onreadystatechange = null; 
alert("Script loaded."); 
} 
}; 
script.src = "file1.js"; 
document.getElementsByTagName("head")[0].appendChild(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 { //Others 
script.onload = function(){ 
callback(); 
}; 
} 
script.src = url; 
document.getElementsByTagName("head")[0].appendChild(script); 
} 
The loadScript() function is used as follows: 
loadScript("file1.js", function(){ 
alert("File is loaded!"); 
});

现在你可以按这种动态方式加载脚本了,但是你仍然需要考虑这些文件的下载顺序。主流浏览器中只有FF和Opera保证脚本的执行顺序和你指定的下载顺序一致,其他浏览器将按照脚本文件从服务器返回的顺序来执行。虽然如此,我们仍然有替代的解决方案:
loadScript("file1.js", function(){ 
loadScript("file2.js", function(){ 
loadScript("file3.js", function(){ 
alert("All files are loaded!"); 
}); 
}); 
});

这样我们就能保证脚本文件的下载顺序严格的按照file1-file2-file3的方式进行。
注明:以上内容来自:<High Performance JavaScript>by Nicholas C. Zakas
Javascript 相关文章推荐
javascript StringBuilder类实现
Dec 22 Javascript
模仿jQuery each函数的链式调用
Jul 22 Javascript
javascript中的一些注意事项 更新中
Dec 06 Javascript
基于jquery实现的省市区级联无ajax
Sep 24 Javascript
使用js获取图片原始尺寸
Dec 03 Javascript
使用JS获取当前地理位置方法汇总
Dec 18 Javascript
JavaScript获得表单target属性的方法
Apr 02 Javascript
详解AngularJS中的表达式使用
Jun 16 Javascript
js替换字符串中所有指定的字符(实现代码)
Aug 17 Javascript
Vue监听事件实现计数点击依次增加的方法
Sep 26 Javascript
微信小程序时间控件picker view使用详解
Dec 28 Javascript
JS函数式编程实现XDM一
Jun 16 Javascript
jquery 关键字“拖曳搜索”之“拖曳”以及 图片“提示自适应放大”效果 的实现
Apr 18 #Javascript
jquery 新手学习常见问题解决方法
Apr 18 #Javascript
javascript 设计模式之单体模式 面向对象学习基础
Apr 18 #Javascript
js 获取子节点函数 (兼容FF与IE)
Apr 18 #Javascript
几个比较实用的JavaScript 测试及效验工具
Apr 18 #Javascript
javascript JSON操作入门实例
Apr 16 #Javascript
javascript对象之内置对象Math使用方法
Apr 16 #Javascript
You might like
PHP 数组遍历方法大全(foreach,list,each)
2010/06/30 PHP
php获取301跳转URL简单实例
2013/12/16 PHP
PHP验证信用卡卡号是否正确函数
2015/05/27 PHP
php+ajax无刷新分页实例详解
2015/12/07 PHP
php 实现进制相互转换
2016/04/07 PHP
PHP基于接口技术实现简单的多态应用完整实例
2017/04/26 PHP
Laravel解决nesting level错误和隐藏index.php的问题
2019/10/12 PHP
gearman中worker常驻后台,导致MySQL server has gone away的解决方法
2020/02/27 PHP
jquery 简单的进度条实现代码
2010/03/11 Javascript
JQuery 应用 JQuery.groupTable.js
2010/12/15 Javascript
Javascript 垃圾收集机制介绍理解
2013/05/14 Javascript
js delete 用法(删除对象属性及变量)
2014/08/24 Javascript
Javascript中arguments和arguments.callee的区别浅析
2015/04/24 Javascript
纯js代码实现未知宽高的元素在指定元素中垂直水平居中显示
2015/09/12 Javascript
JS+Canvas实现的俄罗斯方块游戏完整实例
2016/12/12 Javascript
bootstrap模态框示例代码分享
2017/05/17 Javascript
详解vue嵌套路由-params传递参数
2017/05/23 Javascript
Vue2.0子同级组件之间数据交互方法
2018/02/28 Javascript
详解nodejs解压版安装和配置(带有搭建前端项目脚手架)
2018/12/06 NodeJs
js实现经典贪吃蛇小游戏
2020/03/19 Javascript
VUE实现吸底按钮
2021/03/04 Vue.js
python实现杨辉三角思路
2017/07/14 Python
Python实战之制作天气查询软件
2019/05/14 Python
解决Pycharm 包已经下载,但是运行代码提示找不到模块的问题
2019/08/31 Python
Python基于wordcloud及jieba实现中国地图词云图
2020/06/09 Python
如何用python免费看美剧
2020/08/11 Python
浅析Python 责任链设计模式
2020/09/11 Python
如何基于Django实现上下文章跳转
2020/09/16 Python
Python3获取cookie常用三种方案
2020/10/05 Python
什么是CSS3 HSLA色彩模式?HSLA模拟渐变色条
2016/04/26 HTML / CSS
敬老文明号事迹材料
2014/01/16 职场文书
房产代理公证处委托书
2014/04/04 职场文书
计生工作先进事迹
2014/08/15 职场文书
撤诉状格式范本
2015/05/19 职场文书
大学生支教感言
2015/08/01 职场文书
MySQL系列之十三 MySQL的复制
2021/07/02 MySQL