无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本实行完成。JavaScript 实行过程耗时越久,浏览器等待响运用户输入的韶光就越长。浏览器不才载和实行脚本时涌现壅塞的缘故原由在于,脚本可能会改变页面或 JavaScript 的命名空间,它们对后面页面内容造成影响。一个范例的例子便是在页面中利用document.write()。例如清单 1。
清单 1 JavaScript 代码内嵌示例
当浏览器碰着<script>标签时,当前 HTML 页面无从获知 JavaScript 是否会向<p> 标签添加内容,或引入其他元素,或乃至移除该标签。因此,这时浏览器会停滞处理页面,先实行 JavaScript代码,然后再连续解析和渲染页面。同样的情形也发生在利用 src 属性加载 JavaScript的过程中,浏览器必须先花韶光下载外链文件中的代码,然后解析并实行它。在这个过程中,页面渲染和用户交互完备被壅塞了。
1.脚本位置
HTML 4 规范指出 <script> 标签可以放在 HTML 文档的<head>或<body>中,并许可涌现多次。Web 开拓职员一样平常习气在 <head> 中加载外链的 JavaScript,接着用 <link> 标签用来加载外链的 CSS 文件或者其他页面信息。例如清单 2。
清单 2 低效率脚本位置示例
然而这种常规的做法却隐蔽着严重的性能问题。在清单 2 的示例中,当浏览器解析到 <script> 标签(第 4 行)时,浏览器会停滞解析其后的内容,而优先下载脚本文件,并实行个中的代码,这意味着,其后的 styles.css 样式文件和<body>标签都无法被加载,由于<body>标签无法被加载,那么页面自然就无法渲染了。因此在该 JavaScript 代码完备实行完之前,页面都是一片空缺。图 1 描述了页面加载过程中脚本和样式文件的下载过程。
图 1 JavaScript 文件的加载和实行壅塞其他文件的下载
我们可以创造一个有趣的征象:第一个 JavaScript 文件开始下载,与此同时壅塞了页面其他文件的下载。此外,从 script1.js 下载完成到 script2.js 开始下载前存在一个延时,这段韶光恰好是 script1.js 文件的实行过程。每个文件必须等到前一个文件下载并实行完成才会开始下载。在这些文件逐个下载过程中,用户看到的是一片空缺的页面。
从 IE 8、Firefox 3.5、Safari 4 和 Chrome 2 开始都许可并行下载 JavaScript 文件。这是个好,由于<script>标签不才载外部资源时不会壅塞其他<script>标签。遗憾的是,JavaScript 下载过程仍旧会壅塞其他资源的下载,比如样式文件和图片。只管脚本的下载过程不会相互影响,但页面仍旧必须等待所有 JavaScript 代码下载并实行完成才能连续。因此,只管最新的浏览器通过许可并行下载提高了性能,但问题尚未完备办理,脚本壅塞仍旧是一个问题。
由于脚本会壅塞页面其他资源的下载,因此推举将所有<script>标签尽可能放到<body>标签的底部,以只管即便减少对全体页面下载的影响。例如清单 3
清单 3 推举的代码放置位置示例
这段代码展示了在 HTML 文档中放置<script>标签的推举位置。只管脚本下载会壅塞另一个脚本,但是页面的大部分内容都已经下载完成并显示给了用户,因此页面下载不会显得太慢。
这是优化 JavaScript 的紧张规则:将脚本放在底部。
2.组织脚本由于每个<script>标签初始下载时都会壅塞页面渲染,以是减少页面包含的<script>标签数量有助于改进这一情形。这不仅针对外链脚本,内嵌脚本的数量同样也要限定。浏览器在解析 HTML 页面的过程中每碰着一个<script>标签,都会因实行脚本而导致一定的延时,因此最小化延迟韶光将会明显改进页面的总体性能。
这个问题在处理外链 JavaScript 文件时略有不同。考虑到 HTTP 要求会带来额外的性能开销,因此下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。也便是说,减少页面中外链脚本的数量将会改进性能。
常日一个大型网站或运用须要依赖数个 JavaScript 文件。您可以把多个文件合并成一个,这样只须要引用一个<script>标签,就可以减少性能花费。文件合并的事情可通过离线的打包工具或者一些实时的在线做事来实现。
须要特殊提醒的是,把一段内嵌脚本放在引用外链样式表的<link>之后会导致页面壅塞去等待样式表的下载。这样做是为了确保内嵌脚本在实行时能得到最精确的样式信息。因此,建议不要把内嵌脚本紧跟在<link>标签后面。
3.无壅塞的脚本减少 JavaScript 文件大小并限定 HTTP 要求数在功能丰富的 Web 运用或大型网站上并不总是可行。Web 运用的功能越丰富,所须要的 JavaScript 代码就越多,只管下载单个较大的 JavaScript 文件只产生一次 HTTP 要求,却会锁去世浏览器的一大段韶光。为避免这种情形,须要通过一些特定的技能向页面中逐步加载 JavaScript 文件,这样做在某种程度上来说不会壅塞浏览器。
无壅塞脚本的窍门在于,在页面加载完成后才加载 JavaScript 代码。这就意味着在 window 工具的 onload事宜触发后再下载脚本。有多种办法可以实现这一效果。
3.1延迟加载脚本HTML 4 为<script>标签定义了一个扩展属性:defer。Defer 属性指明本元素所含的脚本不会修正 DOM,因此代码能安全地延迟实行。defer 属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,以是它不是一个空想的跨浏览器办理方案。在其他浏览器中,defer 属性会被直接忽略,因此<script>标签会以默认的办法处理,也便是说会造成壅塞。然而,如果您的目标浏览器支持的话,这仍旧是个有用的办理方案。清单 4 是一个例子
清单 4 defer 属性利用方法示例
<script type=\公众text/javascript\"大众 src=\公众script1.js\"大众 defer></script>
带有 defer 属性的<script>标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到<script>标签时开始下载,但不会实行,直到 DOM 加载完成,即onload事宜触发前才会被实行。当一个带有 defer 属性的 JavaScript 文件下载时,它不会壅塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。
任何带有 defer 属性的<script>元素在 DOM 完成加载之前都不会被实行,无论内嵌或者是外链脚本都是如此。清单 5 的例子展示了defer属性如何影响脚本行为:
清单 5 defer 属性对脚本行为的影响
这段代码在页面处理过程中弹出三次对话框。不支持 defer 属性的浏览器的弹出顺序是:\"大众defer\公众、\"大众script\"大众、\公众load\"大众。而在支持 defer 属性的浏览器上,弹出的顺序则是:\"大众script\公众、\"大众defer\"大众、\"大众load\公众。请把稳,带有 defer 属性的<script>元素不是跟在第二个后面实行,而是在 onload 事宜被触发前被调用。
如果您的目标浏览器只包括 Internet Explorer 和 Firefox 3.5,那么 defer 脚本确实有用。如果您须要支持跨领域的多种浏览器,那么还有更同等的实现办法。
HTML 5 为<script>标签定义了一个新的扩展属性:async。它的浸染和 defer 一样,能够异步地加载和实行脚本,不由于加载脚本而壅塞页面的加载。但是有一点须要把稳,在有 async 的情形下,JavaScript 脚本一旦下载好了就会实行,以是很有可能不是按照原来的顺序来实行的。如果 JavaScript 脚本前后有依赖性,利用 async 就很有可能涌现缺点。
3.2动态脚本元素文档工具模型(DOM)许可您利用 JavaScript 动态创建 HTML 的险些全部文档内容。<script>元素与页面其他元素一样,可以非常随意马虎地通过标准 DOM 函数创建:
清单 6 通过标准 DOM 函数创建<script>元素
新的<script>元素加载 script1.js 源文件。此文件当元素添加到页面之后急速开始下载。此技能的重点在于:无论在何处启动下载,文件的下载和运行都不会壅塞其他页面处理过程。您乃至可以将这些代码放在<head>部分而不会对别的部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。
当文件利用动态脚本节点下载时,返回的代码常日立即实行(除了 Firefox 和 Opera,他们将等待此前的所有动态脚本节点实行完毕)。当脚本是\"大众自运行\"大众类型时,这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接口,则会带来问题。这种情形下,您须要跟踪脚本下载完成并是否准备妥善。可以利用动态 <script> 节点发失事宜得到干系信息。
Firefox、Opera, Chorme 和 Safari 3+会在<script>节点吸收完成之后发出一个 onload 事宜。您可以监听这一事宜,以得到脚本准备好的关照:
清单 7 通过监听 onload 事宜加载 JavaScript 脚本
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 脚本
大多数情形下,您希望调用一个函数就可以实现 JavaScript 文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能:
清单 9 通过函数进行封装
此函数吸收两个参数:JavaScript 文件的 URL,和一个当 JavaScript 吸收完成时触发的回调函数。属性检讨用于决定监视哪种事宜。末了一步,设置 src 属性,并将<script>元素添加至页面。此 loadScript() 函数利用方法如下:
清单 10 loadScript()函数利用方法
您可以在页面中动态加载很多 JavaScript 文件,但要把稳,浏览器不担保文件加载的顺序。所有主流浏览器之中,只有 Firefox 和 Opera 担保脚本按照您指定的顺序实行。其他浏览器将按照做事器返回它们的次序下载并运行不同的代码文件。您可以将下载操作串联在一起以担保他们的次序,如下:
清单 11 通过 loadScript()函数加载多个 JavaScript 脚本
此代码等待 script1.js 可用之后才开始加载 script2.js,等 script2.js 可用之后才开始加载 script3.js。虽然此方法可行,但如果要下载和实行的文件很多,还是有些麻烦。如果多个文件的次序十分主要,更好的办法是将这些文件按照精确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,利用一个大文件并没有什么丢失)。
动态脚本加载是非壅塞 JavaScript 下载中最常用的模式,由于它可以跨浏览器,而且大略易用。
3.3利用 XMLHttpRequest(XHR)工具此技能首先创建一个 XHR 工具,然后下载 JavaScript 文件,接着用一个动态 <script> 元素将 JavaScript 代码注入页面。清单 12 是一个大略的例子:
清单 12 通过 XHR 工具加载 JavaScript 脚本
此代码向做事器发送一个获取 script1.js 文件的 GET 要求。onreadystatechange 事宜处理函数检讨 readyState 是不是 4,然后检讨 HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存相应)。如果收到了一个有效的相应,那么就创建一个新的<script>元素,将它的文本属性设置为从做事器吸收到的 responseText 字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被实行,并准备利用。
这种方法的紧张优点是,您可以下载不立即实行的 JavaScript 代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动实行,这使得您可以推迟实行,直到统统都准备好了。另一个优点是,同样的代码在所有当代浏览器中都不会引发非常。
此方法最紧张的限定是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指\公众内容投递网络(Content Delivery Network)\公众,以是大型网页常日不采取 XHR 脚本注入技能。
4.总结一样平常而言,减少 JavaScript 对性能的影响有以下几种方法:
· A.将所有的<script>标签放到页面底部,也便是</body>闭合标签之前,这能确保在脚本实行前页面已经完成了渲染。
· B.尽可能地合并脚本。页面中的<script>标签越少,加载也就越快,相应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
· C.采取无壅塞下载 JavaScript 脚本的方法:
· a-利用<script>标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本);
· b-利用动态创建的<script>元向来下载并实行代码;
· c-利用 XHR 工具下载 JavaScript 代码并注入页面中。
通过以上策略,可以在很大程度长进步那些须要利用大量 JavaScript 的 Web 网站和运用的实际性能。
看完了,别忘了收藏、转发、点个赞,更别忘了要关注本号!
^_^