欢迎来到 jackNEss'窝窝
I like simple mind

说说 javascript阻塞问题

2011年11月09日

说说 javascript阻塞问题

什么是javascript阻塞

在说 javascript阻塞问题之前,我们先看一个例子:

<head>
	<script type="text/javascript" src="http://www.jackness.org/wp-content/themes/JStyle/images/default/blank.png" _src="file1.js"></script>
	<script type="text/javascript" src="http://www.jackness.org/wp-content/themes/JStyle/images/default/blank.png" _src="file2.js"></script>
	<script type="text/javascript" src="http://www.jackness.org/wp-content/themes/JStyle/images/default/blank.png" _src="file3.js"></script>
	<link rel="stylesheet" href="style.css"/>
</head>	

假设在我们页面的头部加上这样一些 script 和 link 标签,我们知道,当浏览器访问这个页面时,
页面的加载顺序是:

  1. 加载file1.js
  2. 运行file1.js里面的代码
  3. 加载file2.js
  4. 运行file2.js里面的代码
  5. 加载file3.js
  6. 运行file3.js里面的代码
  7. 加载style.css
  8. 读取body标签里面的标签元素并渲染

下面是浏览器解析这个页面时的一个 时间瀑布图:

图中蓝色部分为加载这些文件所花费的时间,而 file1.js 与 file2.js之间的空白,其实是file1.js文件加载完之后,执行file1.js里面的 js所花费的时间,其余的类同。由于浏览器在遇到<body>标签之前,不会渲
染页面的任何部分,在用户访问这个网页的时候,在没加载完上面这些 js代码和 css代码时,我们用户所看到的页面还是空白一片的,我们一般称这些 js代码渲染给页面造成渲染延迟这样一个过程叫做 javascript阻塞。

javascript阻塞坏处

坏处当然,就是由于javascript阻塞让页面产生这样一个空白期,导致用户必须忍受这种可以察觉的延迟,从用户的角度来说,这样是一种十分糟糕的体验来的。

如何防止javascript阻塞

这个是这篇文章重点要讲述的内容(没有之一),根据实现的方式,通过实现 无阻塞脚本 来防止 javascript阻塞 有 3 种方法。

把所有<script>标签放到<body>标签之内的底部

顾名思义,就是将所有脚本<script>标签放到<body>标签之内的底部,从而让页面在主体内容渲染完之前都不会受到 脚本阻塞的影响。

<!-- html document -->
<body>
	<!-- 
	..
	主体代码 
	..
	-->
	
	<script type="text/javascript" src="http://www.jackness.org/wp-content/themes/JStyle/images/default/blank.png" _src="file1.js"></script>
	<script type="text/javascript" src="http://www.jackness.org/wp-content/themes/JStyle/images/default/blank.png" _src="file2.js"></script>
	<script type="text/javascript" src="http://www.jackness.org/wp-content/themes/JStyle/images/default/blank.png" _src="file3.js"></script>
</body>	

缺点

缺点就是如果页面内有部分元素调用到你原本应该放在页头里面的 脚本的时候,这时候就会由于还没渲染到从而导致触发 对象为空这样的一个error,轻则影响页面的健康度,重则影响到页面的正常运作。

通过XMLHttpRequest方式脚本注入(即AJAX)

就是通过 AJAX的方式把所需用的脚本注入到页面上面,这样的加载方式是和页面渲染同步进行的,不存在阻塞和先后问题,所以这种方法是行得通的,然后当这个 js异步传输成功之后 把获取到的文本流 放进一个 <script>标签里面即可。

// javascript document
var xhr = new XMLHttpRequest();
xhr.open("get", "file1.js", true);
xhr.onreadystatechange = function(){
	if (xhr.readyState == 4){
		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);
		}
	}
};
xhr.send(null);

缺点

缺点就是,这种方式的局限性在于所有请求文件必须在同一个域里面,不支持跨域,这意味着我们不能从CDN(Content Delivery Network,即内容分发网络)下载下来,因此,大型的web应用通常不会采取XHR(XMLHttpRequest)脚本注入技术。

动态脚本元素(推荐)

文档对象模型(DOM)允许你使用JavaScript 动态创建HTML 的几乎全部文档内容。其根本在于,<script>元素与页面其他元素没有什么不同:引用变量可以通过DOM 进行检索,可以从文档中移动、删除,也可以被创建。一个新的<script>元素可以非常容易地通过标准DOM 函数创建:

//javascript document
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);	

新的<script>元素加载file1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。你甚至可以将这些代码放在<head>部分而不会对其余部分的页面代码造成影响(除了用于下载文件的HTTP 连接)。

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

Firefox, Opera, Chorme 和Safari 3+会在<script>节点接收完成之后发出一个load 事件。你可以监听这一事件,以得到脚本准备好的通知:

//javascript document
var script = document.createElement("script")
script.type = "text/javascript";
//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
alert("Script loaded!");
};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);

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

  1. “uninitialized” 默认状态
  2. “loading” 下载开始
  3. “loaded” 下载完成
  4. “interactive” 下载完成但尚不可用
  5. “complete” 所有数据已经准备好

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

//javascript document
var script = document.createElement("script")
script.type = "text/javascript";
//Internet Explorer
script.onreadystatechange = function(){
	if (script.readyState == "loaded" || script.readyState == "complete"){
		script.onreadystatechange = null;
		alert("Script loaded.");
		}
	};
script.src = "file1.js";
document.getElementsByTagName("head")[0].appendChild(script);

大多数情况下,你希望调用一个函数就可以实现JavaScript 文件的动态加载。下面的函数封装了标准实现和IE 实现所需的功能:

//javascript document
function loadScript(url, callback){
	var script = document.createElement("script")
	script.type = "text/javascript";
	//IE
	if(script.readyState){ 
		script.onreadystatechange = function(){
			if (script.readyState == "loaded" || script.readyState == "complete"){
				script.onreadystatechange = null;
				callback();
			}
		};
	} 
	//Others
	else{ 
		script.onload = function(){
		callback();
		};
	}
	script.src = url;
	document.getElementsByTagName("head")[0].appendChild(script);
}	

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

//javascript document
loadScript("file1.js", function(){
	alert("File is loaded!");
});
分类javascript
标签
阅读 3,786
  • 评论加载中...

标签云

分类目录

最新留言

  • 评论加载中...

与我联系

如有疑问or建议可通过以下方式跟我取得联系.

Q Q:373435871
Email:jackness1208@gmail.com
© Copyright 2011 - 2014 jackNEss.org All Rights Reserved 粤ICP备14065612号
首页 | 关于我 | 网站地图 | RSS