加载 Javascript 最佳实践


Posted in Javascript onOctober 30, 2011

相信很多与页面打过交道的同学都对 Yahoo 的 Best Practices for Speeding Up Your Web Site 不陌生。而这 35 条最佳实践中,对 Javascript 的加载顺序的要求是:Put Scripts at the Bottom。因为根据 HTTP/1.1 specification 看来,在同一时间加载两个文件是最理想的,而 Javascript 脚本会阻碍平行下载。Steve 说那是 2008 ? 2009 那个时代用的。现在,加载 Javascript 已经有了革命性的化变。

加载 Javascript 最佳实践

在开讲之前,有一个必须解决的问题是:为什么我们要把 JS 文件放在 </body> 之前的最底部。根本原因是,它不能平行下载。而其实并不是所有浏览器都不支持。现在大部分浏览器都支持 Script 的平行下载,除了老掉牙的 IE6&7、Firefox 2&3.0、 Safari 3、Chrome 1。但我们最熟悉的老掉牙同学 IE6 (或以IE为核的那些壳)还是中国(甚至世界上)市场上占用率最高的浏览器,因此我们需要一个折衷的方案。

一、分析

我们有6种方法可以实现平行(NON-Blocking)下载:

  • XHR Eval ? 用 XHR 下载,并 eval() 执行 responseText.。
  • XHR Injection ? 用 XHR 下载,在页面中动态创建一个 script 元素,并将 responseText 作为其 text
  • Script in Iframe ? 把脚本放在 HTML 中,使用 ifame  来下载它。
  • Script DOM Element ? 动态创建一个 script 元素,把 src 指向脚本URL.
  • Script Defer ? 给 script 标添加 defer 属性
  • document.write Script Tag ? 利用 document.write <script src=""> 添加到 HTML 中。但这个只对 IE 有效。

兼容性可看下图:

Technique Parallel Downloads Differ Existing Scripts Busy Indicators Ensures Order Size (bytes)
XHR Eval IE, FF, Saf, Chr, Op no no Saf, Chr - ~500
XHR Injection IE, FF, Saf, Chr, Op no yes Saf, Chr - ~500
Script in Iframe IE, FF, Saf, Chr, Op no no IE, FF, Saf, Chr - ~50
Script DOM Element IE, FF, Saf, Chr, Op yes yes FF, Saf, Chr FF, Op ~200
Script Defer IE, Saf4, Chr2, FF3.1 yes yes IE, FF, Saf, Chr, Op IE, FF, Saf, Chr, Op ~50
document.write Script Tag IE, Saf4, Chr2, Op yes yes IE, FF, Saf, Chr, Op IE, FF, Saf, Chr, Op ~100

二、方案

对于究竟应该使用哪种方案。这完全取决于你需要自身的需要。这张图描述了什么时候使用什么方法:

加载 Javascript 最佳实践

从总体上看来,Script DOM Element 是比较好的方案。NCZ 的博客上提过,目前最好的技术是:

  1. 创建两个 JavaScript  文件。第一个文件只提供动态下载 Javascript 的代码,第二个文件则包含所有其他页面所需脚本的文件。
  2. <script> 在页部(</body> 之前)引入第一个文件。
  3. 创建第二个 <script> 来执行下载第二个 Javascript 文件的函数和其他的初始化代码。

三、实现代码

根据上面的提到的技术。NCZ 推荐第一个文件只包含相应的实现第二个文件动态加载的代码:

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); 
}

然后,我们可以在页面中这样做:
<script type="text/javascript" src="http://your.cdn.com/first.js"></script> 
<script type="text/javascript"> 
loadScript("http://your.cdn.com/second.js", function(){ 
//初始化你的代码 
}); 
</script>

在 HTML5 上,我们可以使用 async 属性。这个 HTML 属性的作用正是我们需要的 NON-Blocking 下载技术。虽然目前支持的浏览器并不多(似乎只有 Firefox 3.6+ ?),但给需要平行下载的 Javascript(按照方案看来,一般是第一个 JS 文件) 加上这个属性,也不会影响其他不支持的浏览器,所以,是推荐使用的。
<script type="text/javascript" async src="foo.js"></script>

四、实践
YUI3 的 Loader 使用了 NCZ 的这样的方法。而在支付宝。我们也使用了类似的方法。这里简单说一下。
<script type="text/javascript" charset="utf-8"> 
// 配置 combo 服务的 PATH 
araleConfig = { 
combo_host: "http://domain.com", 
combo_path: '/path/to/the/compressed/file' 
} 
</script> 
<script type="text/javascript" src="core.js"></script>

在使用的时候,再利用 Loader.use() 来实现代码的动态加载。当然,这里不仅仅是动态加载,还有一定的缓存机制在里面。建议你查看相关的 combo 服务的技术。目前支付宝前端架构组的工友们,已经在这一块取得了一些不错的进展(根据测试报告,速度是非常不错的,可能会在适当的时候开源出来)。

五、总结
前端性能优化方面。还有很多东西可以做。并且,随着 HTML5 技术的出现和 Javascript 技术的不断创新,相信还有更多东西是值得期待。前端们,加油吧,未来有很多东西应该是由你来主导的。

Reference:

  1. Loading Scripts Without Blocking
  2. The best way to load external JavaScript
  3. Evolution of Script Loading
  4. What is a non-blocking script?
  5. HTML5 ? Scripting: async Attribute
Javascript 相关文章推荐
通过正则格式化url查询字符串实现代码
Dec 28 Javascript
用客户端js实现带省略号的分页
Apr 27 Javascript
解决css和js的{}与smarty定界符冲突问题的两种方法
Sep 10 Javascript
ie 7/8不支持trim的属性的解决方案
May 23 Javascript
JavaScript实现自动变换表格边框颜色
May 08 Javascript
JavaScript中Number.NEGATIVE_INFINITY值的使用详解
Jun 05 Javascript
javascript中递归函数用法注意点
Jul 30 Javascript
javascript实现uploadify上传格式以及个数限制
Nov 23 Javascript
JS获取input[file]的值并显示在页面的实现方法
Mar 09 Javascript
vue-cli安装使用流程步骤详解
Nov 08 Javascript
Vue指令v-for遍历输出JavaScript数组及json对象的常见方式小结
Feb 11 Javascript
Vue使用富文本编辑器Vue-Quill-Editor(含图片自定义上传服务、清除复制粘贴样式等)
May 15 Javascript
js判断是否为数组的函数: isArray()
Oct 30 #Javascript
JS trim去空格的最佳实践
Oct 30 #Javascript
js中更短的 Array 类型转换
Oct 30 #Javascript
JavaScript Array Flatten 与递归使用介绍
Oct 30 #Javascript
关于图片按比例自适应缩放的js代码
Oct 30 #Javascript
js 弹出菜单/窗口效果
Oct 30 #Javascript
基于Jquery+Ajax+Json的高效分页实现代码
Oct 29 #Javascript
You might like
目录,文件操作详谈―PHP
2006/11/25 PHP
使用PHPMYADMIN操作mysql数据库添加新用户和数据库的方法
2010/04/02 PHP
php处理文件的小例子(解压缩,删除目录)
2013/02/03 PHP
分享8个最佳的代码片段在线测试网站
2013/06/29 PHP
百度工程师讲PHP函数的实现原理及性能分析(一)
2015/05/13 PHP
PHP版微信小店接口开发实例
2016/11/12 PHP
thinkPHP框架中执行事务的方法示例
2018/05/31 PHP
推荐一些非常不错的javascript学习资源站点
2007/08/29 Javascript
javascript学习(二)javascript常见问题总结
2013/01/02 Javascript
JavaScript中几个重要的属性(this、constructor、prototype)介绍
2013/05/19 Javascript
js实现同一页面多个不同运动效果的方法
2015/04/10 Javascript
js实现根据身份证号自动生成出生日期
2015/12/15 Javascript
延时加载JavaScript代码提高速度
2015/12/27 Javascript
bootstrap-table组合表头的实现方法
2017/09/07 Javascript
JavaScript实现快速排序的方法分析
2018/01/10 Javascript
微信小程序的注册页面包含倒计时验证码、获取用户信息
2019/05/22 Javascript
nodejs一个简单的文件服务器的创建方法
2019/09/13 NodeJs
Vue组件间的通信pubsub-js实现步骤解析
2020/03/11 Javascript
[52:00]2018DOTA2亚洲邀请赛 4.1 小组赛 A组加赛 LGD vs Optic
2018/04/02 DOTA
[43:24]VG vs Serenity 2018国际邀请赛小组赛BO2 第二场 8.17
2018/08/20 DOTA
python多线程之事件Event的使用详解
2018/04/27 Python
python暴力解压rar加密文件过程详解
2019/07/05 Python
Python Matplotlib 基于networkx画关系网络图
2019/07/10 Python
如何基于python实现画不同品种的樱花树
2020/01/03 Python
django model object序列化实例
2020/03/13 Python
Python %r和%s区别代码实例解析
2020/04/03 Python
python3中datetime库,time库以及pandas中的时间函数区别与详解
2020/04/16 Python
python tqdm库的使用
2020/11/30 Python
CSS3教程(4):网页边框和网页文字阴影
2009/04/02 HTML / CSS
HTML5所有标签汇总及标签意义解释
2015/03/12 HTML / CSS
使用html2canvas.js实现页面截图并显示或上传的示例代码
2018/12/18 HTML / CSS
Regatta官网:英国最受欢迎的户外服装和鞋类品牌
2019/05/01 全球购物
继电保护工岗位职责
2014/01/05 职场文书
纪念9.18事变演讲稿
2014/09/14 职场文书
2014年外贸业务员工作总结
2014/12/11 职场文书
2016大学生形势与政策心得体会
2016/01/12 职场文书