`
xphwv
  • 浏览: 43973 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

非阻塞式JavaScript脚本介绍

 
阅读更多

JavaScript 倾向于阻塞浏览器某些处理过程,如HTTP 请求和界面刷新,这是开发者面临的最显著的性能问题。保持JavaScript文件短小,并限制HTTP请求的数量,只是创建反应迅速的网页应用的第一步。一个应用程序所包含的功能越多,所需要的JavaScript 代码就越大,保持源码短小并不总是一种选择。尽管下载一个大JavaScript 文件只产生一次HTTP 请求,却会锁定浏览器一大段时间。为避开这种情况,你需要向页面中逐步添加JavaScript,某种程度上说不会阻塞浏览器。非阻塞脚本的秘密在于,等页面完成加载之后,再加载JavaScript 源码。从技术角度讲,这意味着在window 的load 事件发出之后开始下载代码。有三种方法可以实现这种效果。

延期脚本

HTML 4 为<script>标签定义了一个扩展属性:defer。这个defer 属性指明元素中所包含的脚本不打算修改DOM,因此代码可以稍后执行。defer 属性只被Internet Explorer 4 和Firefox 3.5 更高版本的浏览器所支持,它不是一个理想的跨浏览器解决方案。在其他浏览器上,defer 属性被忽略,<script>标签按照默认方式被处理(造成阻塞)。如果浏览器支持的话,这种方法仍是一种有用的解决方案。示例如下:

复制代码
<script type="text/javascript" src="file1.js" defer></script>
复制代码

一个带有defer 属性的<script>标签可以放置在文档的任何位置。对应的JavaScript 文件将在<script>被解析时启动下载,但代码不会被执行,直到DOM 加载完成。(在onload 事件句柄被调用之前)。当一个defer的JavaScript 文件被下载时,它不会阻塞浏览器的其他处理过程,所以这些文件可以与页面的其他资源一起并行下载。任何带有defer 属性的<script>元素在DOM 加载完成之前不会被执行,不论是内联脚本还是外部脚本文件,都是这样。下面的例子展示了defer 属性如何影响脚本行为:

复制代码
<html>
<head>
<title>Script Defer Example</title>
</head>
<body>
<script defer>
alert(
"defer");
</script>
<script>
alert(
"script");
</script>
<script>
window.onload
=function(){
alert(
"load");
};
</script>
</body>
</html>
复制代码

这些代码在页面处理过程中弹出三个对话框。如果浏览器不支持defer 属性,那么弹出对话框的顺序是"defer","script"和"load"。如果浏览器支持defer 属性,那么弹出对话框的顺序是"script","defer"和"load"。注意,标记为defer 的<script>元素不是跟在第二个后面运行,而是在onload 事件句柄处理之前被调用。如果你的目标浏览器只包括Internet Explorer 和Firefox 3.5,那么defer 脚本确实有用。如果你需要支持跨领域的多种浏览器,那么还有更一致的实现方式。

动态脚本元素

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

复制代码
var script = document.createElement ("script");
script.type
="text/javascript";
script.src
="file1.js";
document.getElementsByTagName_r(
"head")[0].appendChild(script);
复制代码

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

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

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

复制代码
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 有五种取值:

  • "uninitialized"默认状态
  • "loading"下载开始
  • "loaded"下载完成
  • "interactive"下载完成但尚不可用
  • "complete"所有数据已经准备好

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

复制代码
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 实现所需的功能:

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

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

复制代码
loadScript("file1.js", function(){
alert(
"File is loaded!");
});
复制代码

你可以在页面中动态加载很多JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。所有主流浏览器之中,只有Firefox 和Opera 保证脚本按照你指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。你可以将下载操作串联在一起以保证他们的次序,如下:

复制代码
loadScript("file1.js", function(){
loadScript(
"file2.js", function(){
loadScript(
"file3.js", function(){
alert(
"All files are loaded!");
});
});
});
复制代码

此代码等待file1.js 可用之后才开始加载file2.js,等file2.js 可用之后才开始加载file3.js。虽然此方法可行,但如果要下载和执行的文件很多,还是有些麻烦。如果多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,使用一个大文件并没有什么损失)。

动态脚本加载是非阻塞JavaScript 下载中最常用的模式,因为它可以跨浏览器,而且简单易用。

使用XMLHttpRequest(XHR)对象

此技术首先创建一个XHR 对象,然后下载JavaScript 文件,接着用一个动态<script>元素将JavaScript 代码注入页面。下面是一个简单的例子:

复制代码
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);
复制代码

此代码向服务器发送一个获取file1.js 文件的GET 请求。onreadystatechange 事件处理函数检查readyState是不是4,然后检查HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接收到的responseText 字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被执行,并准备使用。这种方法的主要优点是,你可以下载不立即执行的JavaScript 代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得你可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从CDNs 下载(CDN 指"内容投递网络(Content Delivery Network)",大型网页通常不采用XHR 脚本注入技术。

推荐的向页面加载大量JavaScript 的方法分为两个步骤:第一步,包含动态加载JavaScript 所需的代码,然后加载页面初始化所需的除JavaScript 之外的部分。这部分代码尽量小,可能只包含loadScript()函数,它下载和运行非常迅速,不会对页面造成很大干扰。当初始代码准备好之后,用它来加载其余的JavaScript。例如:

复制代码
<script type="text/javascript" src="loader.js"></script>
<script type="text/javascript">
loadScript(
"the-rest.js", function(){
Application.init();
});
</script>
复制代码

将此代码放置在body 的关闭标签</body>之前。这样做有几点好处:首先,像前面讨论过的那样,这样做确保JavaScript 运行不会影响页面其他部分显示。其次,当第二部分JavaScript 文件完成下载,所有应用程序所必须的DOM 已经创建好了,并做好被访问的准备,避免使用额外的事件处理(例如window.onload)来得知页面是否已经准备好了。另一个选择是直接将loadScript()函数嵌入在页面中,这可以避免另一次HTTP 请求。例如:

复制代码
<script type="text/javascript">
function loadScript(url, callback){
var script = document.createElement ("script")
script.type
="text/javascript";
if (script.readyState){ //IE
script.onreadystatechange =function(){
if (script.readyState =="loaded"||
script.readyState
=="complete"){
script.onreadystatechange
=null;
callback();
}
};
}
else { //Others
script.onload =function(){
callback();
};
}
script.src
= url;
document.getElementsByTagName_r(
"head")[0].appendChild(script);
}
loadScript(
"the-rest.js", function(){
Application.init();
});
</script>
复制代码

如果你决定使用这种方法,建议你使用''YUI Compressor''或者类似的工具将初始化脚本缩小到最小字节尺寸。一旦页面初始化代码下载完成,你还可以使用loadScript()函数加载页面所需的额外功能函数。

http://www.cnblogs.com/jenry/archive/2011/02/13/1953211.html

分享到:
评论

相关推荐

    让浏览器非阻塞加载javascript的几种方法小结

    这就需要我们使用一些方法来让浏览器以非阻塞的方式加载外部脚本。 一 使用XMLHttpRequest对象异步方式加载外部脚本。  这种方式好处是触发较少的浏览器忙指示器,能够被所有现代浏览器的所支持

    Node.js技术参考手册

    它使用了一个事件驱动、非阻塞式I/O模型,使得JavaScript可以与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。Node.js对一些特殊用例进行优化,提供替代的API,使得V8在非浏览器环境下运行得更好,V8引擎...

    CondiLoader:JavaScript和CSS的条件异步加载程序脚本

    该脚本旨在在网页上进行非阻塞条件JavaScript和CSS加载。 它接受项目数组,并且对于每个项目检查,是否在页面上显示具有指定选择器的元素或xpath。 如果是这样,脚本将加载指定CSS和JavaScript,调用init函数或使用...

    node-v12.16.0-x64.msi

    Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。 [1] Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。 [2] 发布于2009年...

    node-v10.15.2-x64.zip

    Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。 [1] Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。 [2] 发布于2009年...

    NodeJS API文档

    Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。发布于2009年5月,由Ryan...

    node-v12.14.0.pkg

    Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。 [1] Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。 [2] 发布于2009...

    Javascript无阻塞加载具体方式

    3、非阻塞脚本 等页面完成加载后,再加载js代码。也就是,在window.load事件发出后开始下载代码。 (1)defer属性:支持IE4和fierfox3.5更高版本浏览器 [removed]…[removed] 内联和外部文件 带defer属性

    node-v12.16.2-x64.zip

    Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。 [1] Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。 [2] 发布于2009年...

    node-v10.16.1-linux-x64.tar.xz

    Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。 Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。 [2] 发布于2009年5月...

    win7最后一个支持的nodejs-13.14.0版本附历史更新地址

    Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型。 Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等服务端语言平起平坐的脚本语言。 发布于2009年5月,由...

    stan-loader:一个非常简单的非渲染阻塞 javascript 加载器,它将按顺序加载一组库,然后在加载或错误时执行回调函数

    一个非常简单的非渲染阻塞 javascript 加载器,它将按顺序加载一组库,然后在加载或错误时执行回调函数。 将允许在 、 、 和上获得更高的分数。 它是什么/它是如何工作的 STAN Loader 是一个接受四个参数的函数; ...

    非阻塞动态加载javascript广告实现代码

    代码如下: &lt;!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” ... &lt;head&gt; &lt;... charset=utf-8″ /&gt; &lt;...广告代码最后加载&lt;...div id=”ope

    node-v18.16.0-win-x64

    是一个基于Chrome V8引擎的JavaScript运行环境,使用了一个事件驱动、非阻塞式I/O模型, [1] 让JavaScript 运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。...

    01-Node.js介绍.zip

    课程简介: Node.js是一个Javascript运行环境(runtime environment),发布于2009年5月,由Ryan ...Node.js 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。

    JavaScript王者归来part.1 总数2

     3.1 我能用什么来编写脚本--适合编写JavaScript的文本编辑器   3.2 来自浏览器的支持   3.3 集成开发环境   3.4 调试工具--提升开发效率的利器   3.5 定位代码和调用堆栈   3.6 浏览器捕获异常   3.7 ...

    如何提高javascript加载速度

    方法如下: 1、将所有[removed]标签放在尽可能接近&lt;body&gt;标签底部的位置,以保证页面在脚本运行之前完成解析尽量减少对整个页面下载的影响...4、非阻塞方式下载脚本。等页面完成加载之后,再加载 JavaScript 源码

    node.js v10.7.0 64位

    Node 是一个让 JavaScript 运行在服务端的开发平台,它让 JavaScript 成为与PHP、Python、Perl、Ruby 等...Node 使用事件驱动, 非阻塞I/O 模型而得以轻量和高效,非常适合在分布式设备上运行数据密集型的实时应用。

    delayed-js:通过动态加载和依赖跟踪来延迟 JavaScript 文件和脚本的加载

    Delayed JS 是一种轻量级解决方案,旨在通过动态加载和依赖跟踪来延迟 JavaScript 文件和脚本的加载,而无需外部加载大型库。 它有助于防止页面呈现阻塞,从而使页面加载速度更快,并满足谷歌的页面速度建议。 使用...

Global site tag (gtag.js) - Google Analytics