编写高性能的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实现自动发送邮件
Oct 21 Javascript
js从外部获取图片的实现方法
Aug 05 Javascript
Angularjs手动解析表达式($parse)
Oct 12 Javascript
详解js的异步编程技术的方法
Feb 09 Javascript
Bootstrap 模态对话框只加载一次 remote 数据的完美解决办法
Jul 09 Javascript
关于javascript sort()排序你可能忽略的一点理解
Jul 18 Javascript
Node.js如何使用Diffie-Hellman密钥交换算法详解
Sep 05 Javascript
jQuery实现的点击按钮改变样式功能示例
Jul 21 jQuery
vscode中vue-cli项目es-lint的配置方法
Jul 30 Javascript
vue-quill-editor的使用及个性化定制操作
Aug 04 Javascript
PHP 502bad gateway原因及解决方案
Nov 13 Javascript
ESLint 是如何检查 .vue 文件的
Nov 30 Vue.js
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获取url字符串截取路径的文件名和扩展名的函数
2010/01/22 PHP
PHP微信开发之文本自动回复
2016/06/23 PHP
Symfony2创建基于域名的路由相关示例
2016/11/14 PHP
swoole锁的机制代码实例讲解
2021/03/04 PHP
限制文本字节数js代码
2007/03/06 Javascript
ASP.NET jQuery 实例9  通过控件hyperlink实现返回顶部效果
2012/02/03 Javascript
jQuery中读取json文件示例代码
2013/05/10 Javascript
JavaScript中的原型链prototype介绍
2014/12/30 Javascript
网页中JS函数自动执行常用三种方法
2016/03/30 Javascript
第三篇Bootstrap网格基础
2016/06/21 Javascript
jquery.zclip轻量级复制失效问题
2017/01/08 Javascript
JS实现复制内容到剪贴板功能
2017/02/05 Javascript
Angular2 组件通信的实例代码
2017/06/23 Javascript
微信小程序地图(map)组件点击(tap)获取经纬度的方法
2019/01/10 Javascript
Python常见文件操作的函数示例代码
2011/11/15 Python
Python发送Email方法实例
2014/08/21 Python
简单介绍Python中的JSON模块
2015/04/08 Python
python实现简单的socket server实例
2015/04/29 Python
使用Django Form解决表单数据无法动态刷新的两种方法
2017/07/14 Python
Python cookbook(数据结构与算法)实现优先级队列的方法示例
2018/02/18 Python
Python3 导入上级目录中的模块实例
2019/02/16 Python
pandas实现将dataframe满足某一条件的值选出
2019/06/12 Python
Python 动态导入对象,importlib.import_module()的使用方法
2019/08/28 Python
python3.6、opencv安装环境搭建过程(图文教程)
2019/11/05 Python
python代码如何实现余弦相似性计算
2020/02/09 Python
python 伯努利分布详解
2020/02/25 Python
Python利用socket模块开发简单的端口扫描工具的实现
2021/01/27 Python
浅谈Html5页面打开app的一些思考
2020/03/30 HTML / CSS
英国、欧洲和全球租车服务:Avis英国
2016/08/29 全球购物
联想新西兰官方网站:Lenovo New Zealand
2018/10/30 全球购物
英国最受欢迎的平价女士时装零售商:Roman Originals
2019/11/02 全球购物
暑期教师培训方案
2014/06/07 职场文书
2014年工作总结与下年工作计划
2014/11/27 职场文书
党员反邪教心得体会
2016/01/15 职场文书
教你用Java在个人电脑上实现微信扫码支付
2021/06/13 Java/Android
Win11 Dev 预览版25174.1000发布 (附更新修复内容汇总)
2022/08/05 数码科技