JS具有阻塞特性,当浏览器在执行js代码时,不能同时做其它事情,即<script>每次出现都会让页面等待脚本的解析和执行(不论JS是内嵌的还是外链的),JS代码执行完成后,才继续渲染页面。

由于,JS的这种阻塞特性,每次遇到<script>,页面都必须停下来等待脚本下载并执行,这会停止页面绘制,带来不好的用户体验。所以,有必要减少JS阻塞特性造成的困扰。

1 优化脚本位置

HTML4规范中,<script>可以放在<head>或<body>中。你可能习惯性的在<head>中放置多个外链JS、CSS,以求优先加载它们。浏览器在继续到<body>之前,不会渲染页面,所以,把JS放在<head>中,会导致延迟。为了提高用户体验,新一代浏览器都支持并行下载JS,但是JS下载仍然会阻塞其它资源的下载(eg.图片)。尽管脚本的下载过程并不会相互影响,但页面仍然必须等待所有JS下载并执行完成才能继续。显见,所有<script>应该尽可能放到<body>的底部,以减少对页面下载的影响。

注意:CSS文件本身是并行下载,不会阻塞页面的其他进程。但是,如果把一段内嵌脚本放在引用外链CSS的<link>之后会导致页面阻塞去等待CSS的下载。这样做是为了确保内嵌脚本在执行时能够获得正确的样式信息。所以,最好不要把内嵌脚本放在CSS的<link>之后。

2 减少外链脚本数量以改善性能

原因很简单,额外的HTTP请求会带来额外的开销,所以减少页面中外链脚本的数量,有助于改善性能。

3 使用无阻塞下载JS方法

无阻塞脚本的秘诀在于,在页面加载完成后才加载JS,即在window对象的load事件触发后在下载脚本。

3.1 使用<script>的defer属性(仅IE和Firefox3.5以上);

defer属性指明本元素所含的脚本不会修改DOM,因此代码能安全的延迟执行。defer属性的<script>,对应的JS文件将在页面解析到<script>时开始下载,但并不会执行,直到DOM加载完成,即onload事件触发前被调用。当一个带有defer属性的JS文件下载时,他不会阻塞浏览器的其它进程,因此这类文件可以与页面中的其他资源并行下载。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DeferredScripts</title>
</head>
<body> <script type="text/javascript" defer>
alert("defer");
</script> <script type="text/javascript">
alert("script");
</script> <script type="text/javascript">
window.onload = function() {
alert("load");
};
</script> </body>
</html>

对于支持defer的浏览器弹出顺序是:script>defer>load;而不支持该属性的浏览器的弹出顺序为:defer>script>load。

3.2 使用动态创建的<script>元素来下载并执行代码

实例代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DynamicScriptElements</title>
</head>
<body> <script type="text/javascript">
function loadScript(url, callback) {
var script = document.createElement("Script");
script.type = "text/javascript"; //IE 验证脚本是否下载完成
if (script.readyState) {
script.onreadystatechange = function() {
//readyState属性有5种取值
//uninitialized:初始状态
//loading:开始下载
//interactive:数据完成下载但尚不可用
//complete:数据已经准备就绪
//实际使用时,readyState的值并不像我们预想的那样有规律,实践发现使用readyState
//最靠谱的方式是同时检查以下2个状态,只要其中1个触发,就认为脚本下载完成。
if (script.readyState == "loaded" || script.readyState == "complete") {
//移除事件处理器,确保事件不会处理2次
script.onreadystatechange = null;
callback();
}
};
} //其他浏览器
else {
script.onload = function() {
callback();
};
} script.src = url;
//把新建的<Script>添加到<head>里比添加到<body>里更保险。
document.getElementsByTagName("head")[0].appendChild(script);
} //动态加载多个JS文件
//优先加载Common.js,等待Common.js加载完毕后加载Costom.js
//不同浏览器的执行顺序不同
//Firefox、Opera能够保证按照你脚本的加载顺序来执行
//其他浏览器会按照从服务端返回的顺序执行代码,因此使用嵌套的方法保证调用顺序
loadScript("Common.js", function() {
loadScript("Costom.js", function() {
alert("all load");
});
});
</script> </body>
</html>

文件在该元素被添加到页面时开始下载。这种技术的重点在于:无论在何时启动下载,文件的下载与执行不会阻塞页面的其他进程。使用动态脚本节点下载文件时,根据浏览器不同,多数浏览器,返回的代码会立即执行(Firefox、Opera,会等待此前所有动态节点执行完毕)。当脚本”自执行“时,这种机制运行正常,但是当代码内只包含供其它脚本调用的接口时,就必须确保脚本下载完成并准备就绪,在上例中列举了不同浏览器的验证方法。

注意:如果多个文件的顺序很重要,更好的做法是把它们按正确顺序合并为一个文件。此外,说把新建的<Script>添加到<head>里比添加到<body>里更保险是因为要尽量避免页面报错(在低版本的IE中使用不当会发生"操作已中止"错误

3.3 使用XHR对象下载JS代码并注入页面中

实例代码如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>XhrScriptInjection</title>
</head>
<body> <script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open("get", "JScript.js", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
//2XX表示有效响应,304表示从缓存读取
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); //一旦新创建的<script>被添加到页面,代码就立刻执行然后准备就绪。
}
}
};
xhr.send(null);
</script> </body>
</html>

这种方法的优点是,你可以下载JS代码但不立即执行。由于代码是在<script>标签之外返回的,因此它下载后不会自动执行,这使得你可以把脚本的执行推迟到你准备好的时候。另一个优点是,同样的代码在所有主流浏览器中都能正常工作。

这种方法的主要局限性是JS文件必须与所有请求的页面处于相同的域。
综上所述,向页面中添加大量JS的推荐做法只需两步:先添加动态加载的所需代码,然后加载初始化页面所需的剩下代码。

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

    <script type="text/javascript">
loadScript("Costom.js", function() {
//Do Something
});
</script>

优化前:

优化后:

"操作已中止"错误

<html>
<head>
<title>Operation Aborted Example</title>
</head>
<body>
<p>The following code should cause an Operation Aborted error in IE versions prior to 8.</p>
<div>
<script type="text/javascript">
document.body.appendChild(document.createElement("div"));
</script>
</div>
</body>
</html>

上述代码在低版本IE中会报"操作已中止"错误。出现此问题的原因是子容器 HTML 元素包含试图修改父容器元素的子容器的脚本。脚本试图使用 innerHTML 方法或 appendChild 方法修改父容器元素。例如对于如果 DIV 元素是一个 BODY 元素中的子容器,并且在 DIV 元素中的一个 SCRIPT 块试图修改 DIV 元素的父容器的 BODY 元素可能会出现此问题。

最简单的解决方法:将脚本移到body元素的范围。

<html>
<head>
<title>Operation Aborted Example</title>
</head>
<body>
<p>The following code should cause an Operation Aborted error in IE versions prior to 8.</p>
<div>
</div>
<script type="text/javascript">
document.body.appendChild(document.createElement("div"));
</script>
</body>
</html>

其它解决方法可以参考:

http://www.nczonline.net/blog/2008/03/17/the-dreaded-operation-aborted-error

http://support.microsoft.com/kb/927917/zh-cn

作者:MeteorSeed

感谢您阅读本文,如果您觉得有所收获,麻烦点一下右边的“推荐”,您的支持是对我最大的鼓励...

转载请注明出处。

javascript笔记——js的阻塞特性[转载]的更多相关文章

  1. js的阻塞特性

    JS具有阻塞特性,当浏览器在执行js代码时,不能同时做其它事情,即<script>每次出现都会让页面等待脚本的解析和执行(不论JS是内嵌的还是外链的),JS代码执行完成后,才继续渲染页面. ...

  2. javascript笔记——js面试问题

    1:javascript中的变量提升(先使用再声明,js中的函数也存在函数提升) 2:js中的事件捕获和事件冒泡(是两个相反的过程) 3:js中的动画队列(比如animate动画没有在点击的时候阻止正 ...

  3. javascript笔记——js获取input标签中光标的索引

    出处:http://www.cnblogs.com/MrZouJian/p/5850553.html function getTxt1CursorPosition(){ var oTxt1 = doc ...

  4. [Effective JavaScript笔记]第1条:了解使用的js版本

    1997年 正式成为国际标准,官方名称为ECMAScript. 1999年 定稿第3版ECMAScript标准(简称ES3),最广泛的js版本. 2009年 发布第5版即ES5,引入了一些新特性,标准 ...

  5. JavaScript【5】高级特性(作用域、闭包、对象)

    笔记来自<Node.js开发指南>BYVoid编著 1.作用域 if (true) { var somevar = 'value'; } console.log(somevar); Jav ...

  6. [Effective JavaScript 笔记]第3章:使用函数--个人总结

    前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...

  7. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  8. web性能优化之---JavaScript中的无阻塞加载性能优化方案

    一.js阻塞特性 JS 有个很无语的阻塞特性,就是当浏览器在执行JS 代码时,不能同时做其他任何事情,无论其代码是内嵌的还是外部的. 即<script>每次出现都会让页面等待脚本的解析和执 ...

  9. 外部JS的阻塞下载

    转载于:http://www.cnblogs.com/mofish/archive/2011/09/29/2195256.html 所有浏览器在下载JS的时候,会阻止一切其他活动,比如其他资源的下载, ...

随机推荐

  1. Codeforces Gym 100286I iSharp 水题

    Problem I. iSharpTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/ ...

  2. C#传递参数到线程的n个方法

    [转]http://kb.cnblogs.com/a/888688/ 本片文章的议题是有关于传递参数到线程的几种方法. 首先我们要知道什么是线程,什么时候要用到线程,如何去使用线程,如何更好的利用线程 ...

  3. jquery中获取当前点击对象

    jquery中获取当前点击对象的简单方法就是,在点击事件click中传入event对象 click(function(event)); 调用当前对象就是$(event.target);

  4. Python学习 之 正则表达式

    1.简单的正则表达式 import re s=r'abc' re.findall(s,"aaaaaaaaaaaaaaa") #结果为[] re.findall(s,"ab ...

  5. Cache和Buffer的区别

    一.研究数据库的人这样理解:http://wenku.baidu.com/view/32b8b13e376baf1ffc4fad7e.html Cache和Buffer是两个不同的概念,简单的说,Ca ...

  6. nginx.conf配置及优化相关

    nginx.conf配置文件内容 user www www; worker_processes ; worker_rlimit_nofile ; error_log /data/nginx/logs/ ...

  7. Java 并发专题 : Timer的缺陷 用ScheduledExecutorService替代

    继续并发,上篇博客对于ScheduledThreadPoolExecutor没有进行介绍,说过会和Timer一直单独写一篇Blog. 1.Timer管理延时任务的缺陷 a.以前在项目中也经常使用定时器 ...

  8. Adobe Edge Animate --使用HTML5实现手机摇一摇功能

    Adobe Edge Animate --使用HTML5实现手机摇一摇功能 版权声明: 本文版权属于 北京联友天下科技发展有限公司. 转载的时候请注明版权和原文地址. HTML5的发展日新月异,其功能 ...

  9. Html5中的video元素

    最近在做门户的时候遇到要显示企业的视频介绍,需要找到一个在aspx页面播放视频的html,最后找到了是一段HTML5最新的html代码,如下: /// <summary> /// 播放视频 ...

  10. css3 背景记

    css3 背景 css背景主要包括五个属性: background-color background-color:transparent || <color> 用来设置元素的背景颜色,默认 ...