编写高性能的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 脚本将当地时间转换成其它时区
Mar 19 Javascript
Jquery遍历节点的方法小集
Jan 22 Javascript
JS实现点击链接取消跳转效果的方法
Jan 24 Javascript
浅析javascript的间隔调用和延时调用
Nov 12 Javascript
使用javascript实现Iframe自适应高度
Dec 24 Javascript
JS实现文字放大效果的方法
Mar 03 Javascript
JavaScript 弹出子窗体并返回结果到父窗体的实现代码
May 28 Javascript
详解在Vue中如何使用axios跨域访问数据
Jul 07 Javascript
JavaScript闭包与作用域链实例分析
Jan 21 Javascript
js页面加载后执行的几种方式小结
Jan 30 Javascript
如何用JS实现简单的数据监听
May 06 Javascript
JavaScript选择器函数querySelector和querySelectorAll
Nov 27 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
Body是什么,该怎么喝出咖啡里的口感
2021/03/03 咖啡文化
打造计数器DIY三步曲(中)
2006/10/09 PHP
PHP 字符串分割和比较
2009/10/06 PHP
linux下安装php的memcached客户端
2014/08/03 PHP
ExtJS的FieldSet的column列布局
2009/11/20 Javascript
40款非常有用的 jQuery 插件推荐(系列一)
2011/12/21 Javascript
js和jquery对dom节点的操作(创建/追加)
2013/04/21 Javascript
手写的一个兼容各种浏览器的javascript getStyle函数(获取元素的样式)
2014/06/06 Javascript
多种方法实现360浏览器下禁止自动填写用户名密码
2014/06/16 Javascript
js propertychange和oninput事件
2014/09/28 Javascript
javascript中html字符串转化为jquery dom对象的方法
2015/08/27 Javascript
jQuery插件Echarts实现的双轴图效果示例【附demo源码下载】
2017/03/04 Javascript
浅谈Fetch 数据交互方式
2018/12/20 Javascript
详解Vue路由自动注入实践
2019/04/17 Javascript
angular多语言配置详解
2019/05/16 Javascript
Python实现的一个找零钱的小程序代码分享
2014/08/25 Python
python实现的简单抽奖系统实例
2015/05/22 Python
深入理解python中的闭包和装饰器
2016/06/12 Python
Python中.py文件打包成exe可执行文件详解
2017/03/22 Python
Python向日志输出中添加上下文信息
2017/05/24 Python
pandas DataFrame实现几列数据合并成为新的一列方法
2018/06/08 Python
python在openstreetmap地图上绘制路线图的实现
2019/07/11 Python
python 制作python包,封装成可用模块教程
2020/07/13 Python
3分钟看懂Python后端必须知道的Django的信号机制
2020/07/26 Python
python如何实现图片压缩
2020/09/11 Python
CSS3径向渐变之大鱼吃小鱼之孤单的大鱼
2016/04/26 HTML / CSS
毕业生幼师求职自荐信
2013/10/01 职场文书
黄河象教学反思
2014/02/10 职场文书
酒店总经理职务说明书
2014/02/26 职场文书
乡镇领导班子四风对照检查材料
2014/09/27 职场文书
庆祝国庆节标语
2014/10/09 职场文书
2015年公司行政后勤工作总结
2015/05/20 职场文书
换届选举主持词
2015/07/03 职场文书
趣味运动会标语口号
2015/12/26 职场文书
2016年优秀共青团员事迹材料
2016/02/25 职场文书
五年级作文之成长
2019/09/16 职场文书