JavaScript的性能优化:加载和执行

     js最大的问题是:不管当前JavaScript代码是内嵌仍是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。JavaScript执行过程耗时越久,浏览器等待响应用户输入的时间就越长。浏览器在下载和执行脚本时出现阻塞.javascript

解决办法:css

一:脚本的位置:html

因为脚本会阻塞页面其余资源的下载,所以推荐将全部<script>标签尽量放到<body>标签的底部,以尽可能减小对整个页面下载的影响。java

例如:浏览器

<html>  
<head>  
    <title>Source Example</title>  
    <link rel="stylesheet" type="text/css" href="styles.css">  
</head>  
<body>  
    <p>Hello world!</p>  
    <!-- Example of efficient script positioning -->  
    <script type="text/javascript" src="script1.js"></script>  
    <script type="text/javascript" src="script2.js"></script>  
    <script type="text/javascript" src="script3.js"></script>  
</body>  
</html>

二: 组织脚本 缓存

因为每一个<script>标签初始下载时都会阻塞页面渲染,因此减小页面包含的<script>标签数量有助于改善这一状况。这不只针对外链脚本,内嵌脚本的数量一样也要限制。浏览器在解析 HTML 页面的过程当中每遇到一个<script>标签,都会因执行脚本而致使必定的延时,所以最小化延迟时间将会明显改善页面的整体性能。安全

这个问题在处理外链 JavaScript 文件时略有不一样。考虑到 HTTP 请求会带来额外的性能开销,所以下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。也就是说,减小页面中外链脚本的数量将会改善性能。服务器

因此尽可能合并和压缩js网络

三:延迟加载脚本app

HTML 4 为<script>标签订义了一个扩展属性:deferDefer 属性指明本元素所含的脚本不会修改 DOM,所以代码能安全地延迟执行

用法:

<script type="text/javascript" src="script1.js" defer></script>

HTML 5 为<script> 标签订义了一个新的扩展属性:async 。它的做用和 defer  同样,可以异步地加载和执行脚本,不由于加载脚本而阻塞页面的加载。可是有一点须要注意,在有 async  的状况下,JavaScript 脚本一旦下载好了就会执行,因此颇有可能不是按照本来的顺序来执行的。若是 JavaScript 脚本先后有依赖性,使用 async  就颇有可能出现错误。

用法:

<script type="text/javascript" src="script1.js" async></script>

四:动态脚本元素

文档对象模型(DOM)容许您使用 JavaScript 动态建立 HTML 的几乎所有文档内容。<script>元素与页面其余元素同样,能够很是容易地经过标准 DOM 函数建立:

清单 6 经过标准 DOM 函数建立<script>元素

Js代码 

  1. var script = document.createElement ("script");  

  2.    script.type = "text/javascript";  

  3.    script.src = "script1.js";  

  4.    document.getElementsByTagName("head")[0].appendChild(script);  

新的<script>元素加载 script1.js 源文件。此文件当元素添加到页面以后马上开始下载。此技术的重点在于:不管在何处启动下载,文件的下载和运行都不会阻塞其余页面处理过程。您甚至能够将这些代码放在<head>部分而不会对其他部分的页面代码形成影响(除了用于下载文件的 HTTP 链接)。

当文件使用动态脚本节点下载时,返回的代码一般当即执行(除了 Firefox 和 Opera,他们将等待此前的全部动态脚本节点执行完毕)。当脚本是“自运行”类型时,这一机制运行正常,可是若是脚本只包含供页面其余脚本调用调用的接口,则会带来问题。这种状况下,您须要跟踪脚本下载完成并是否准备妥善。可使用动态 <script> 节点发出事件获得相关信息。

Firefox、Opera, Chorme 和 Safari 3+会在<script>节点接收完成以后发出一个 onload 事件。您能够监听这一事件,以获得脚本准备好的通知:

清单 7 经过监听 onload 事件加载 JavaScript 脚本

Js代码 

  1. var script = document.createElement ("script")  

  2. script.type = "text/javascript";  

  3. //Firefox, Opera, Chrome, Safari 3+  

  4. script.onload = function(){  

  5.     alert("Script loaded!");  

  6. };  

  7. script.src = "script1.js";  

  8. document.getElementsByTagName("head")[0].appendChild(script);  

 Internet Explorer 支持另外一种实现方式,它发出一个 readystatechange 事件。<script>元素有一个readyState 属性,它的值随着下载外部文件的过程而改变。readyState 有五种取值:

  • “uninitialized”:默认状态

  • “loading”:下载开始

  • “loaded”:下载完成

  • “interactive”:下载完成但尚不可用

  • “complete”:全部数据已经准备好

微软文档上说,在<script>元素的生命周期中,readyState 的这些取值不必定所有出现,但并无指出哪些取值总会被用到。实践中,咱们最感兴趣的是“loaded”和“complete”状态。Internet Explorer 对这两个 readyState 值所表示的最终状态并不一致,有时<script>元素会获得“loader”却从不出现“complete”,但另一些状况下出现“complete”而用不到“loaded”。最安全的办法就是在readystatechange 事件中检查这两种状态,而且当其中一种状态出现时,删除readystatechange事件句柄(保证事件不会被处理两次):

清单 8 经过检查readyState状态加载JavaScript脚本

Js代码 

  1. var script = document.createElement("script")  

  2. script.type = "text/javascript";  

  3. //Internet Explorer  

  4. script.onreadystatechange = function(){  

  5.      if (script.readyState == "loaded" || script.readyState == "complete"){  

  6.            script.onreadystatechange = null;  

  7.            alert("Script loaded.");  

  8.      }  

  9. };  

  10. script.src = "script1.js";  

  11. document.getElementsByTagName("head")[0].appendChild(script);  

大多数状况下,您但愿调用一个函数就能够实现JavaScript文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能:

清单 9 经过函数进行封装

Js代码 

  1. function loadScript(url, callback){  

  2.     var script = document.createElement ("script")  

  3.     script.type = "text/javascript";  

  4.     if (script.readyState){ //IE  

  5.         script.onreadystatechange = function(){  

  6.             if (script.readyState == "loaded" || script.readyState == "complete"){  

  7.                 script.onreadystatechange = null;  

  8.                 callback();  

  9.             }  

  10.         };  

  11.     } else { //Others  

  12.         script.onload = function(){  

  13.             callback();  

  14.         };  

  15.     }  

  16.     script.src = url;  

  17.     document.getElementsByTagName("head")[0].appendChild(script);  

  18. }  

此函数接收两个参数:JavaScript 文件的 URL,和一个当 JavaScript 接收完成时触发的回调函数。属性检查用于决定监视哪一种事件。最后一步,设置 src 属性,并将<script>元素添加至页面。此loadScript() 函数使用方法以下:

清单 10 loadScript()函数使用方法

Js代码 

  1. loadScript("script1.js"function(){  

  2.     alert("File is loaded!");  

  3. });  

您能够在页面中动态加载不少 JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。全部主流浏览器之中,只有 Firefox 和 Opera 保证脚本按照您指定的顺序执行。其余浏览器将按照服务器返回它们的次序下载并运行不一样的代码文件。您能够将下载操做串联在一块儿以保证他们的次序,以下:

清单 11 经过 loadScript()函数加载多个JavaScript脚本

Js代码 

  1. loadScript("script1.js"function(){  

  2.     loadScript("script2.js"function(){  

  3.         loadScript("script3.js"function(){  

  4.             alert("All files are loaded!");  

  5.         });  

  6.     });  

  7. });  

此代码等待 script1.js 可用以后才开始加载 script2.js,等 script2.js 可用以后才开始加载 script3.js。虽然此方法可行,但若是要下载和执行的文件不少,仍是有些麻烦。若是多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序链接成一个文件。独立文件能够一次性下载全部代码(因为这是异步进行的,使用一个大文件并无什么损失)。

动态脚本加载是非阻塞 JavaScript 下载中最经常使用的模式,由于它能够跨浏览器,并且简单易用。

使用XMLHttpRequest(XHR)对象

此技术首先建立一个 XHR 对象,而后下载 JavaScript 文件,接着用一个动态 <script> 元素将 JavaScript 代码注入页面。清单 12 是一个简单的例子:

清单 12 经过 XHR 对象加载 JavaScript 脚本

Js代码 

  1. var xhr = new XMLHttpRequest();  

  2. xhr.open("get""script1.js"true);  

  3. xhr.onreadystatechange = function(){  

  4.     if (xhr.readyState == 4){  

  5.         if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){  

  6.             var script = document.createElement ("script");  

  7.             script.type = "text/javascript";  

  8.             script.text = xhr.responseText;  

  9.             document.body.appendChild(script);  

  10.         }  

  11.     }  

  12. };  

  13. xhr.send(null);  

此代码向服务器发送一个获取 script1.js 文件的 GET 请求。onreadystatechange 事件处理函数检查readyState 是否是 4,而后检查 HTTP 状态码是否是有效(2XX 表示有效的回应,304 表示一个缓存响应)。若是收到了一个有效的响应,那么就建立一个新的<script>元素,将它的文本属性设置为从服务器接收到的 responseText 字符串。这样作实际上会建立一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被执行,并准备使用。 

这种方法的主要优势是,您能够下载不当即执行的 JavaScript 代码。因为代码返回在<script>标签以外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得您能够推迟执行,直到一切都准备好了。另外一个优势是,一样的代码在全部现代浏览器中都不会引起异常。 

此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指”内容投递网络(Content Delivery Network)”,因此大型网页一般不采用 XHR 脚本注入技术。 

总结

减小 JavaScript 对性能的影响有如下几种方法:

  • 将全部的<script>标签放到页面底部,也就是</body>闭合标签以前,这能确保在脚本执行前页面已经完成了渲染。

  • 尽量地合并脚本。页面中的<script>标签越少,加载也就越快,响应也越迅速。不管是外链脚本仍是内嵌脚本都是如此。

  • 采用无阻塞下载 JavaScript 脚本的方法:

  • 使用<script>标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);

  • 使用动态建立的<script>元素来下载并执行代码;

  • 使用 XHR 对象下载 JavaScript 代码并注入页面中。

经过以上策略,能够在很大程度上提升那些须要使用大量 JavaScript 的 Web 网站和应用的实际性能。 


相关网址:

http://www.cnblogs.com/snandy/archive/2011/04/26/2029537.html

http://www.cnblogs.com/_franky/archive/2010/06/20/1761370.html

http://www.360doc.com/content/15/0208/17/9200790_447256181.shtml

http://developer.51cto.com/art/201210/361913.htm

相关文章
相关标签/搜索