高性能Javascript(1)
第一部分 加载与运行
<html>
<head>
<title>Script Example</title>
</head>
<body>
<p>
<script type="text/javascript">
document.write("The date is " + (new Date()).toDateString());
</script>
</p>
</body>
</html>
当浏览器遇到一个<script>标签时,正如上面HTML页面中那样,无法预知JavaScript是否在<p>标签中添加内容。因此,浏览器停下来,运行此JavaScript代码,然后再继续解析、翻译页面。同样的事情发生在使用src属性加载JavaScript的过程中。浏览器必须首先下载外部文件的代码,这要占用一些时间,然后解析并运行此代码。此过程中,页面解析和用户交互是被完全阻塞的。
为了保持代码的相似性,我们尽量将相同的代码组织在一起,例如:
<html>
<head>
<title>Script Example</title>
<-- Example of inefficient script positioning -->
<script type="text/javascript" src="file1.js"></script>
<script type="text/javascript" src="file2.js"></script>
<script type="text/javascript" src="file3.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<p>Hello world!</p>
</body>
</html>
这些看起来比较规整的代码有一个明显的性能问题就是:在<head>部分加载了三个JavaScript文件。因为每个<script>标签阻塞了页面的解析过程,直到它完整地下载并运行了外部JavaScript代码之后,页面处理才能继续进行。用户必须忍受这种可以察觉的延迟。请记住,浏览器在遇到<body>标签之前,不会渲染页面的任何部分。用这种方法把脚本放在页面的顶端,将导致一个可以察觉的延迟,通常表现为:页面打开时,首先显示为一幅空白的页面,而此时用户即不能阅读,也不能与页面进行交互操作。为了更好地理解此过程,我们使用瀑布图来描绘每个资源的下载过程。图1-1显示出页面加载过程中,每个脚本文件和样式表文件下载的过程。
第一个JavaScript文件开始下载,并阻塞了其他文件的下载过程。进一步,在file1.js下载完之后和file2.js开始下载之前有一个延时,这是file1.js完全运行所需的时间。每个
文件必须等待前一个文件下载完成并运行完之后,才能开始自己的下载过程。当这些文件下载时,用户面对一个空白的屏幕。这就是今天大多数浏览器的行为模式。Internet Explorer 8, Firefox 3.5, Safari 4, 和Chrome 2允许并行下载JavaScript文件。这个好消息表明,当一个<script>标签正在下载外部资源时,不必阻塞其他<script>标签。不幸的是,JavaScript的下载仍然要阻塞其他资源的下载过程,例如图片。即使脚本之间的下载过程互不阻塞,页面仍旧要等待所有JavaScript代码下载并执行完成之后才能继续。所以,当浏览器通过允许并行下载提高性能之后,该问题并没有完全解决。因为脚本阻塞其他页面资源的下载过程,所以推荐的办法是:将所有<script>标签放在尽可能接近<body>标签底部的位置,尽量减少对整个页面下载的影响。例如
<html>
<head>
<title>Script Example</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<p>Hello world!</p>
<-- Example of recommended script positioning -->
<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>标签在HTML文件中的位置。尽管脚本下载之间互相阻塞,但页面已经下载完成并且显示在用户面前了,进入页面的速度不会显得太慢。这正是“Yahoo! 优越性能小组”关于JavaScript的第一条定律:将脚本放在底部。
另外,浏览器请求4个25K的外部JS文件的速度要慢于请求1个100K的js文件,因此,我们应该尽量将JS文件包含在一个中加载。(每个HTTP请求都会产生额外的性能负担,下载
一个100KB的文件比下载四个25KB的文件要快)。
1.1 Nonblocking Scripts 非阻塞脚本
Deferred Scripts 延期脚本
HTML 4为<script>标签定义了一个扩展属性:defer。这个defer属性指明元素中所包含的脚本不打算修改DOM,因此代码可以稍后执行。defer属性只被Internet Explorer 4和Firefox 3.5更高版本的浏览器所支持,它不是一个理想的跨浏览器解决方案。在其他浏览器上,defer属性被忽略,<script>标签按照默认方式被处理(造成阻塞)。如果浏览器支持的话,这种方法仍是一种有用的解决方案。示例如下:
<script type="text/javascript" src="file1.js" defer></script>
一个带有defer属性的<script>标签可以放置在文档的任何位置。对应的JavaScript文件将在<script>被解时启动下载,但代码不会被执行,直到DOM加载完成(在onload事件句柄被调用之前)。当一个defer的JavaScript文件被下载时,它不会阻塞浏览器的其他处理过程,所以这些文件可以与页面的其他资源一起并行下载.(需要理解的是一个<script>脚本的执行时间包含两部分,(1)请求加载时间(2)JS执行时间。defer的作用是延迟JS的执行时间。任何带有defer属性的<script>元素在DOM加载完成之前不会被执行,不论是内联脚本还是外部脚本文件,都是这样。下面的例子展示了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>
这些代码在页面处理过程中弹出三个对话框。如果浏览器不支持defer属性,那么弹出对话框的顺序是“defer”,“script”和“load”。如果浏览器支持defer属性,那么弹出对话框的顺序是“script”,“defer”和“load”。注意,标记为defer的<script>元素不是跟在第二个后面运行,而是在onload事件句柄处理之前被调用。
Dynamic Script Elements 动态脚本元素
文档对象模型(DOM)允许你使用JavaScript动态创建HTML的几乎全部文档内容。其根本在于,<script>元素与页面其他元素没有什么不同:引用变量可以通过DOM进行检索,可以从文档中移动、删除,也可以被创建。一个新的<script>元素可以非常容易地通过标准DOM函数创建
var script = document.createElement ("script");
script.type = "text/javascript";
script.src = "file1.js";
document.getElementsByTagName_r("head")[0].appendChild(script);
新的<script>元素加载file1.js源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。
你甚至可以将这些代码放在<head>部分而不会对其余部分的页面代码造成影响(除了用于下载文件的HTTP连接)
大多数情况下,你希望调用一个函数就可以实现JavaScript文件的动态加载。下面的函数封装了标准实现和IE实现所需的功能:
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_r("head")[0].appendChild(script);
}
1.2 XMLHttpRequest Script Injection XHR脚本注入
另一个以非阻塞方式获得脚本的方法是使用XMLHttpRequest(XHR)对象将脚本注入到页面中。此技术首先创建一个XHR对象,然后下载JavaScript文件,接着用一个动态<script>元素将JavaScript代码注入页面。下面是一个简单的例子:
var xhr = new XMLHttpRequest();
xhr.open("get", "file1.js", true);
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
var script = document.createElement ("script");
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
};
xhr.send(null);
此代码向服务器发送一个获取file1.js文件的GET请求。onreadystatechange事件处理函数检查readyState是不是4,然后检查HTTP状态码是不是有效(2XX表示有效的回应,304表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接收到的responseText字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被执行,并准备使用。
这种方法的主要优点是,你可以下载不立即执行的JavaScript代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得你可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。此方法最主要的限制是:JavaScript文件必须与页面放置在同一个域内,不能从CDNs下载(CDN指“内容投递网络(Content Delivery Network)”,前面002篇《成组脚本》一节提到)。正因为这个原因,大型网页通常不采用XHR脚本注入技术。
1.3 Recommended Nonblocking Pattern 推荐的非阻塞模式
推荐的向页面加载大量JavaScript的方法分为两个步骤:第一步,包含动态加载JavaScript所需的代码,然后加载页面初始化所需的除JavaScript之外的部分。这部分代码尽量小,可能只包含loadScript()函数,它下载和运行非常迅速,不会对页面造成很大干扰。当初始代码准备好之后,用它来加载其余的JavaScript。
例如:
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript">
loadScript("the-rest.js", function(){
Application.init();
});
</script>
将此代码放置在body的关闭标签</body>之前。这样做有几点好处:首先,像前面讨论过的那样,这样做确保JavaScript运行不会影响页面其他部分显示。其次,当第二部分JavaScript文件完成下载,所有应用程序所必须的DOM已经创建好了,并做好被访问的准备,避免使用额外的事件处理(例如window.onload)来得知页面是否已经准备好了
1.4 The LazyLoad library && The LABjs library
作为一个更通用的工具,Yahoo! Search的Ryan Grove创建了LazyLoad库(参见http://github.com/rgrove/lazyload/)。LazyLoad是一个更强大的loadScript()函数。LazyLoad精缩之后只有大约1.5KB(精缩,而不是用gzip压缩的)。具体用法不再赘述,请参考详细文档说明。
另一个非阻塞JavaScript加载库是LABjs(http://labjs.com/),Kyle Simpson写的一个开源库,由Steve Souders赞助。此库对加载过程进行更精细的控制,并尝试并行下载尽可能多的代码。LABjs也相当小,只有4.50KB(精缩,而不是用gzip压缩的),所以具有最小的页面代码尺寸
Summary
管理浏览器中的JavaScript代码是个棘手的问题,因为代码执行阻塞了其他浏览器处理过程,诸如用界面绘制。每次遇到<script>标签,页面必须停下来等待代码下载(如果是外部的)并执行,然后再继续处理页面其他部分。但是,有几种方法可以减少JavaScript对性能的影响:
[1] 将所有<script>标签放置在页面的底部,紧靠body关闭标签</body>的上方。此法可以保证页面在脚本运行之前完成解析。
[2] 将脚本成组打包。页面的<script>标签越少,页面的加载速度就越快,响应也更加迅速。不论外部脚本文件还是内联代码都是如此.
[3] 有几种方法可以使用非阻塞方式下载JavaScript:
——为<script>标签添加defer属性(只适用于Internet Explorer和Firefox 3.5以上版本)
——动态创建<script>元素,用它下载并执行代码
——用XHR对象下载代码,并注入到页面中
MORE REFERANCE
http://coolshell.cn/articles/9749.html
高性能Javascript(1)的更多相关文章
- 《高性能javascript》一书要点和延伸(上)
前些天收到了HTML5中国送来的<高性能javascript>一书,便打算将其做为假期消遣,顺便也写篇文章记录下书中一些要点. 个人觉得本书很值得中低级别的前端朋友阅读,会有很多意想不到的 ...
- 《高性能javascript》 领悟随笔之-------DOM编程篇(二)
<高性能javascript> 领悟随笔之-------DOM编程篇二 序:在javaSctipt中,ECMASCRIPT规定了它的语法,BOM实现了页面与浏览器的交互,而DOM则承载着整 ...
- 《高性能javascript》 领悟随笔之-------DOM编程篇
<高性能javascript> 领悟随笔之-------DOM编程篇一 序:在javaSctipt中,ECMASCRIPT规定了它的语法,BOM实现了页面与浏览器的交互,而DOM则承载着整 ...
- 各种JS模板引擎对比数据(高性能JavaScript模板引擎)
最近做了JS模板引擎测试,拿各个JS模板引擎在不同浏览器上去运行同一程序,下面是模板引擎测试数据:通过测试artTemplate.juicer与doT引擎模板整体性能要有绝对优势: js模板引擎 Ja ...
- 高性能javascript学习笔记系列(6) -ajax
参考 高性能javascript javascript高级程序设计 ajax基础 ajax技术的核心是XMLHttpRequest对象(XHR),通过XHR我们就可以实现无需刷新页面就能从服务器端读 ...
- 高性能javascript学习笔记系列(5) -快速响应的用户界面和编程实践
参考高性能javascript 理解浏览器UI线程 用于执行javascript和更新用户界面的进程通常被称为浏览器UI线程 UI线程的工作机制可以理解为一个简单的队列系统,队列中的任务按顺序执行 ...
- 高性能JavaScript 编程实践
前言 最近在翻<高性能JavaScript>这本书(2010年版 丁琛译),感觉可能是因为浏览器引擎的改进或是其他原因,书中有些原本能提高性能的代码在最新的浏览器中已经失效.但是有些章节的 ...
- 高性能javascript学习笔记系列(4) -算法和流程控制
参考高性能javascript for in 循环 使用它可以遍历对象的属性名,但是每次的操作都会搜索实例或者原型的属性 导致使用for in 进行遍历会产生更多的开销 书中提到不要使用for in ...
- 高性能javascript学习笔记系列(3) -DOM编程
参考 高性能javascript 文档对象模型(DOM)是独立于语言的,用于操作XML和HTML文档的程序接口API,在浏览器中主要通过DOM提供的API与HTML进行交互,浏览器通常会把DOM和ja ...
- 高性能javascript学习笔记系列(2)-数据存取
参考 高性能javascript Tom大叔深入理解javascript系列 相关概念 1.执行上下文 当控制器转到ecmascript可执行代码的时候,就会进入一个执行上下文,执行上下文是以堆栈 ...
随机推荐
- SpringMVC异常处理注解@ExceptionHandler@ControllerAdvice@ResponseStatus
参考: http://blog.csdn.net/w372426096/article/details/78429132 http://blog.csdn.net/w372426096/article ...
- mydumper备份原理和使用方法
mydumper介绍 MySQL自身的mysqldump工具支持单线程工作,依次一个个导出多个表,没有一个并行的机,这就使得它无法迅速的备份数据. mydumper作为一个实用工具,能够良好支持多线程 ...
- centos7.2 使用rpm安装jdk8
1.下载JDK 去jdk下载页面找到要下载的jdk,用wget下载 wget --no-check-certificate --no-cookies --header "Cookie: or ...
- 如何修改 FastAdmin 弹窗大小?
如何修改 FastAdmin 弹窗大小? 参考代码 1 如下: buttons: [ { name: 'start', , , , extend: 'data-area=\'["350px& ...
- 在Visual Studio代码中使用Flask
Flask是一个用于Web应用程序的轻量级Python框架,它提供了URL路由和页面呈现的基础知识. Flask被称为“微”框架,因为它不直接提供表单验证,数据库抽象,身份验证等功能.这些功能由称为F ...
- JS高级-原型等概念深入理解
一 数据类型: 基本(值)数据类型: string number undefined null boolean 对象(引用)类型 [ 查找对象的属性时,会查找原型链 设置属性时,一般在构造函数里面设置 ...
- VMware虚拟机安装CentOS6.4、部署web项目全过程(设置固定IP、安装JDK、Tomcat、Redis、部署项目)
概述:该篇随笔介绍了在VMware上安装centOS.在centOS上安装JDK.安装Tomcat.安装Redis并部署项目的全过程,虽然参考了很多优秀的文章,但实践.整理.补充都很用心,若要复制粘贴 ...
- C#获得窗口控件句柄
/*整个Windows编程的基础.一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象和同类中的不同的实例,诸如,一个窗口,按钮,图标,滚动条, ...
- 命令行方式删除文件 && 文件夹
del File: 删除文件 rmdir Folder : 删除文件夹 [貌似只能删除空目录] rm -rf Folder: 删除非空文件夹 [用windows自带的cmd提示我“rm”不是内部命令 ...
- python 字符串的一些方法
总结:# split 分割 ********# strip 脱 默认脱头尾的空格 ********# replace 替换 ********# join 插入 拼接 ********# format ...