文档章节

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

EDIAGD
 EDIAGD
发布于 2013/09/07 09:56
字数 2547
阅读 2385
收藏 202

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

解决办法:

一:脚本的位置:

由于脚本会阻塞页面其他资源的下载,因此推荐将所有<script>标签尽可能放到<body>标签的底部,以尽量减少对整个页面下载的影响。

例如:

 

<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

三:延迟加载脚本

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://e22a.com/h.05ApkG?cv=AAKHZXVo&sm=339944

https://shop119727980.taobao.com/?spm=0.0.0.0 

© 著作权归作者所有

EDIAGD
粉丝 50
博文 149
码字总数 58327
作品 0
嘉定
后端工程师
私信 提问
加载中

评论(14)

EDIAGD
EDIAGD 博主
其实我写博客的目的,只是做一下笔记,方便自己查阅,提高效率
打杂程序猿
打杂程序猿

引用来自“Jacktom2012”的评论

代码中居然有:
<script type="text/javascript" src="script1.js"
<span style="font-family:Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace;font-size:14px;line-height:25.1875px;background-color:#F7F7F7;">async</span>></script>

JS标签能够这样用?这不是瞎搞吗?

copy 过来的文章.........居然标了原创..
打杂程序猿
打杂程序猿

引用来自“Jacktom2012”的评论

别讨论了,抄袭的文章,见:
http://kb.cnblogs.com/page/186781/

复制粘贴居然把部分内容粘贴掉了,shit

http://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/index.html

这个才是原文...
打杂程序猿
打杂程序猿
@红薯 原文是IBM的文章.. http://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/index.html
打杂程序猿
打杂程序猿
@红薯 ..这种转载不标明作者的.....不推荐的吧...
苏生不惑
苏生不惑
楼主分享的不错
完颜阿骨再打
完颜阿骨再打
别讨论了,抄袭的文章,见:
http://kb.cnblogs.com/page/186781/

复制粘贴居然把部分内容粘贴掉了,shit
完颜阿骨再打
完颜阿骨再打
代码中居然有:
<script type="text/javascript" src="script1.js"
<span style="font-family:Monaco, Consolas, 'Andale Mono', 'DejaVu Sans Mono', monospace;font-size:14px;line-height:25.1875px;background-color:#F7F7F7;">async</span>></script>

JS标签能够这样用?这不是瞎搞吗?
leixu2
leixu2
学习了。。话说我们的js是有这么做的。。看来真的是有必要。
奥神Well
奥神Well
学习学习。
前端性能优化:细说JavaScript的加载与执行

本文主要是从性能优化的角度来探讨JavaScript在加载与执行过程中的优化思路与实践方法,既是细说,文中在涉及原理性的地方,不免会多说几句,还望各位读者保持耐心,仔细理解,请相信,您的耐...

小白师兄
2018/06/19
0
0
异步加载script,提高前端性能(defer和async属性的区别)

一、异步加载script的好处 为了加快首屏响应速度,前端会采用代码切割、按需加载等方式优化性能。异步加载script也是一种前端优化的手段。 就好比如果我的页面其中一个功能需要打开地图,但是地...

xiaobe
2018/08/22
0
0
[译] 2019 年的 JavaScript 性能

本文翻译自 V8 官方博客的这篇《The Cost Of JavaScript in 2019》,原文作者:Addy Osmani (@addyosmani[1])。 建议阅读本文前先读完这篇文章:使用Script-Streaming提升页面加载性能 首发于...

mogic
07/08
0
0
javascript性能优化:创建javascript无阻塞脚本

  javaScript 在浏览器中的运行性能,在web2.0时代显得尤为重要,成千上万行javaScript代码无疑会成为性能杀手, 在较低版本的浏览器执行JavaScript代码的时候,由于浏览器只使用单一进程来...

grootzhang
2016/04/13
0
0
10 款用于提升 WordPress 性能的插件

WordPress 已经成为最受欢迎的博客以及网站内容管理系统(没有之一),WordPress 最强大的在于其庞大的开发者社区,为 WP 开发出很多的插件,本文向大家介绍 10 款用于提升 WP 性能的插件。 ...

红薯
2011/07/11
1K
4

没有更多内容

加载失败,请刷新页面

加载更多

OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
5
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
6
0
数据库中间件MyCat

什么是MyCat? 查看官网的介绍是这样说的 一个彻底开源的,面向企业应用开发的大数据库集群 支持事务、ACID、可以替代MySQL的加强版数据库 一个可以视为MySQL集群的企业级数据库,用来替代昂贵...

沉浮_
昨天
6
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
7
0
常用物流快递单号查询接口种类及对接方法

目前快递查询接口有两种方式可以对接,一是和顺丰、圆通、中通、天天、韵达、德邦这些快递公司一一对接接口,二是和快递鸟这样第三方集成接口一次性对接多家常用快递。第一种耗费时间长,但是...

程序的小猿
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部