加载 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 相关文章推荐
javascript两段代码,两个小技巧
Feb 04 Javascript
jQuery Ajax方法调用 Asp.Net WebService 的详细实例代码
Apr 27 Javascript
jQuery EasyUI API 中文文档 - TimeSpinner时间微调器
Oct 23 Javascript
JavaScript实现的石头剪刀布游戏源码分享
Aug 22 Javascript
javascript创建cookie、读取cookie
Mar 31 Javascript
jQuery操作iframe中js函数的方法小结
Jul 06 Javascript
在DWR中实现直接获取一个JAVA类的返回值的两种方法
Dec 25 Javascript
Bootstrap源码解读标签、徽章、缩略图和警示框(8)
Dec 26 Javascript
jQuery实现优雅的弹窗效果(6)
Feb 08 Javascript
在vue项目中,将juery设置为全局变量的方法
Sep 25 Javascript
原生JS实现天气预报
Jun 16 Javascript
微信小程序用户授权最佳实践指南
May 08 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+DBM的同学录程序(3)
2006/10/09 PHP
PHP字符串的编码问题的详细介绍
2013/04/27 PHP
Thinkphp中的volist标签用法简介
2014/06/18 PHP
ThinkPHP应用模式扩展详解
2014/07/16 PHP
PHP策略模式定义与用法示例
2017/07/27 PHP
js精度溢出解决方案
2012/12/02 Javascript
JavaScript动态添加css样式和script标签
2016/07/19 Javascript
js实时获取窗口大小变化的实例代码
2016/11/18 Javascript
基于JS实现9种不同的面包屑和分布式多步骤导航效果
2017/02/21 Javascript
微信小程序 转发功能的实现
2017/08/04 Javascript
vue2.0使用swiper组件实现轮播的示例代码
2018/03/03 Javascript
vue左右侧联动滚动的实现代码
2018/06/06 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【凹多边形的分离轴检测算法】
2018/12/13 Javascript
使用pkg打包ThinkJS项目的方法步骤
2019/12/30 Javascript
Vue 组件注册全解析
2020/12/17 Vue.js
[00:12]DAC2018 天才少年转战三号位,他的SOLO是否仍如昔日般强大?
2018/04/06 DOTA
使用pdb模块调试Python程序实例
2015/06/02 Python
用python记录运行pid,并在需要时kill掉它们的实例
2017/01/16 Python
基于Python和Scikit-Learn的机器学习探索
2017/10/16 Python
Python 字符串换行的多种方式
2018/09/06 Python
Python使用sklearn库实现的各种分类算法简单应用小结
2019/07/04 Python
python爬虫 urllib模块发起post请求过程解析
2019/08/20 Python
10个Python面试常问的问题(小结)
2019/11/20 Python
python 实现目录复制的三种小结
2019/12/04 Python
python函数enumerate,operator和Counter使用技巧实例小结
2020/02/22 Python
Python单例模式的四种创建方式实例解析
2020/03/04 Python
Python实现AES加密,解密的两种方法
2020/10/03 Python
python如何获得list或numpy数组中最大元素对应的索引
2020/11/16 Python
详解CSS透明opacity和IE各版本透明度滤镜filter的最准确用法
2016/12/20 HTML / CSS
html5中canvas学习笔记1-画板的尺寸与实际显示尺寸
2013/01/06 HTML / CSS
移动端html5模拟长按事件的实现方法
2018/09/30 HTML / CSS
大学生农村教师实习自我鉴定
2013/09/21 职场文书
大学生新闻专业个人自我评价
2013/11/12 职场文书
珍爱生命演讲稿
2014/05/10 职场文书
PyTorch 如何检查模型梯度是否可导
2021/06/05 Python
mysql中DCL常用的用户和权限控制
2022/03/31 MySQL