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>

js的阻塞特性的更多相关文章

  1. javascript笔记——js的阻塞特性[转载]

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

  2. Ext JS 6 新特性和工具

    Ext JS 6 新特性和工具 Ext JS 6 带来很多新特性.工具和改进.以下是一些亮点: • 合并了 Ext JS & Sencha Touch - 在 Ext 6, 你可以访问 Ext ...

  3. emwin 之 GUI_MessageBox 阻塞特性

    2019-03-01 [小记] GUI_MessageBox 函数执行后必须手动点击关闭窗口,未关闭窗口前线程将阻塞在此处等待关闭窗口事件 [使用场景] 由于该函数不会产生任何消息, 所以可利用阻塞特 ...

  4. 利用js对象的特性,去掉数组中的重复项

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  5. 外部JS的阻塞下载

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

  6. JS异步阻塞的迷思

    还是百度前端技术学院的“任务十九”可视化排序算法的题,在写出快速排序算法之后,要求用动画的形式把这个排序过程呈现出来.排序过程在CPU里不过是瞬间的事,但要转换成“缓慢的”动画效果给人类看,就不得不把 ...

  7. 高性能 js -- 无阻塞加载脚本

    参考: <<高性能JavaScript>> Nicbolas C. Zakas 著 javascript代码的下载和执行过程会阻塞浏览器的其他进程, 比如页面的绘制, 遇到&l ...

  8. 3-0 js基础 语言特性及性能优化

    1.语言特性: 内存泄露:内存没有释放,越堆越多. 垃圾回收(生命周期): 1.局部 很短 在局部中当函数完成时.已经释放了.全局变量在页面关闭的时候才被回收. 2.全局 很长 3.闭包.可长可短,只 ...

  9. Ext JS 4 新特性2:配置项属性(config)之二

    Ext JS 4 新特征2:配置项属性config之二 ☞ Config(自动的setters和getters) Ext JS 4介绍了config声明方式,在Ext JS 中也有几个例子:在运行程序 ...

随机推荐

  1. 01背包dp+并查集 Codeforces Round #383 (Div. 2)

    http://codeforces.com/contest/742/problem/D 题目大意:有n个人,每个人有重量wi和魅力值bi.然后又有m对朋友关系,朋友关系是传递的,如果a和b是朋友,b和 ...

  2. 常用JS调试工具使用方法,帮你快速定位问题(Firebug+ IE“开发人员工具”)

    来源: 这里花了点时间小结了下目前项目中比较合适易于上手的JS调试工具.方法.优点与不足以及一些调试相关功能要点或策略,分享给同学们,只当抛砖引玉了,欢迎大家讨论补充. 一.Firebug:如果项目可 ...

  3. 浅谈 zookeeper 原理,安装和配置

    当前云计算流行, 单一机器额的处理能力已经不能满足我们的需求,不得不采用大量的服务集群.服务集群对外提供服务的过程中,有很多的配置需要随时更新,服务间需要协调工作,那么这些信息如何推送到各个节点?并且 ...

  4. C Runtime Library来历, API, MFC, ATL关系

    首先说明,我google了半天,想找到英文的关于这个资料,但是实在找不到,只好转载国人的讨论. CRT原先是指Microsoft开发的C Runtime Library,用于操作系统的开发及运行.后来 ...

  5. 实现apk 调用framework java JNI中方法

    首先整个实现需要有Android源码编译环境.这里我用的是froyo2.2. 1.JNI层--C++代码部分 在目录frameworks/base/core/jni 下创建android_jnidem ...

  6. js 放大镜用法bug解决

    <img id="zoom_02" src='img/zhang5.jpg' data-zoom-image="img/zhang5p.jpg" /> ...

  7. JavaScript高级程序设计:第十章

    一.理解包含不同层次节点的DOM 1.节点层次 以下面的HTML为例: <html> <head> <title>Sample Page</title> ...

  8. LeetCode OJ 27. Remove Element

    Given an array and a value, remove all instances of that value in place and return the new length. D ...

  9. H5之重力感应篇

    手机的重力感应支持里,有两个主要的事件: 1. OrientationChange (在屏幕发生翻转的时候触发) 2. DeviceOrientation+DeviceMotion(重力感应与陀螺仪) ...

  10. iOS申请真机调试证书 -- 图文详解

    请参考这篇文章 : http://ios.9tech.cn/news/2013/1011/33117.html 这篇文章完全就是对的,主要是最后一步 “配置Xcode" 图没有配全,也配得不 ...