`
xphwv
  • 浏览: 45731 次
  • 性别: 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脚本程序设计

    在"JavaScript脚本程序设计"中,我们可能涉及到以下几个关键知识点: 1. **基础语法**:JavaScript的基础包括变量声明(var、let、const)、数据类型(如字符串、数字、布尔值、null、undefined、对象、数组、符号...

    javascript脚本技术课件

    Node.js基于Chrome的V8引擎,提供了一个强大的运行时环境,支持非阻塞I/O和事件驱动,适合构建高性能的网络应用。 通过深入学习这些知识点,初学者可以逐步掌握JavaScript脚本技术,为成为Web开发者打下坚实的基础...

    js jquery 用户操作行为 非阻塞式 信息/消息提示框

    在JavaScript和jQuery的世界里,创建一个非阻塞式的用户操作行为和信息/消息提示框是提升用户体验的关键。传统的`alert()`函数虽然简单易用,但它会强制暂停页面执行,直到用户点击“确定”按钮,这在很多场景下并不...

    Unity3D脚本中文教程Javascript版本

    8. **Coroutines(协同程序)与Yield**:协同程序允许非阻塞式执行,通过`yield return null;`或`yield return new WaitForSeconds(time);`来暂停执行,直到满足特定条件后继续。 9. **用C#编写脚本**:Unity同时...

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

    这种方法是直接通过JavaScript的`document.write`函数动态写入`&lt;script&gt;`标签,实现脚本的非阻塞加载。同样,由于浏览器兼容性问题,不推荐在现代网页开发中广泛使用。 在选择合适的非阻塞加载方法时,需要考虑...

    JavaScript编程实用脚本

    它们在处理非阻塞I/O操作,如Ajax请求和定时任务时起到重要作用。理解并掌握这些异步处理机制对于编写高性能的前端应用至关重要。 JavaScript还提供了丰富的内置对象和API,例如Array、Date、Math,以及用于DOM操作...

    javascript脚本

    8. **Node.js**:Node.js让JavaScript可以在服务器端运行,提供了一种高效、非阻塞的I/O模型,常用于构建实时的、可伸缩的网络应用。 9. **Web API和Web组件**:Web API如Fetch API用于网络请求,WebSocket实现双向...

    线程之舞:JavaScript的单线程与非阻塞I/O揭秘

    ### 线程之舞:JavaScript的单线程与非阻塞I/O揭秘 #### JavaScript简介及主要特点 JavaScript(通常简称为JS)是一种高级、解释型的编程语言,广泛应用于构建交互式的Web应用程序。随着时间的推移,JavaScript已...

    常用javascript脚本,网站开发人员的手册

    7. **异步编程**:JavaScript的非阻塞特性使得异步编程成为可能,常见的有回调函数、Promise(ES6新增)、async/await(ES7新增)等机制。 8. **模块化**:随着项目复杂度增加,模块化变得至关重要。CommonJS和ES6...

    javascript脚本解压中、英文版

    "脚本解压"通常指的是使用JavaScript实现对压缩文件(如ZIP或RAR)进行解压缩的功能。在这个主题中,我们将深入探讨JavaScript如何处理中、英文版的解压缩任务。 首先,JavaScript本身并不直接支持文件的压缩和解...

    javascript 介绍

    - **异步编程**:JavaScript是单线程的,但它使用事件循环和回调函数、Promise以及现在的async/await来处理异步操作,以实现非阻塞I/O。 - **模块系统**:ES6引入了模块系统,通过`import`和`export`关键字管理代码...

    常用javascript脚本

    JavaScript不仅仅局限于前端,Node.js允许我们在服务器端使用JavaScript,提供了强大的非阻塞I/O模型和丰富的模块生态系统,如Express框架用于构建Web应用,MongoDB用于数据库操作。 以上就是“常用javascript脚本...

    i茅台,葫芦娃预约脚本.zip

    在本案例中,"i茅台预约脚本"可能利用了JavaScript的异步处理能力,以非阻塞的方式执行预约操作,避免因为等待响应而冻结用户界面。 接着,我们来看看"葫芦娃"的含义。这可能是对脚本的一种命名方式,或者暗示脚本...

    LABjs无阻塞脚本加载工具

    LABjs,全称为"Load And Balance JavaScript",是一款强大的JavaScript库,专为了解决Web页面中的脚本阻塞问题而设计。在传统的HTML文档中,脚本通常按顺序执行,这意味着一个脚本的加载和执行会阻塞后续脚本的加载...

    [高性能JavaScript编程].(High.Performance.JavaScript).Nicholas.C.Zakas.文字版

    在讨论非阻塞脚本时,作者推荐了将JavaScript代码放到页面的底部,或者使用动态创建script元素的方法来避免阻塞页面的解析。 书中还介绍了使用XMLHttpRequest进行脚本注入的技术,这是一种异步加载JavaScript文件的...

    nodejs 服务器端的JavaScript脚本(node.js).zip

    这个"nodejs 服务器端的JavaScript脚本(node.js)"压缩包可能包含了一系列的示例代码、教程或者项目,旨在帮助用户理解如何在服务器端运用JavaScript。 在Node.js中,JavaScript不再局限于浏览器环境,而是可以用来...

    JavaScript+jQuery交互式Web前端开发-源代码.zip

    JavaScript,一种轻量级的脚本语言,通常在浏览器端运行,用于增加网页的动态性。它允许开发者通过修改页面内容、处理用户输入、与服务器通信等方式来提升用户体验。例如,你可以使用 JavaScript 来实现网页上的表单...

    head first JavaScript源码

    这些技术解决了JavaScript的非阻塞I/O问题,使得在网络请求、定时任务等场景下,代码能够更加高效地运行。 JavaScript与Web API的结合也十分紧密,例如AJAX(异步JavaScript和XML)用于实现页面的无刷新更新,Web ...

    script的async属性以非阻塞的模式加载脚本

    `defer`属性与`async`类似,也是为了非阻塞加载脚本,但它保证了脚本的执行顺序。当`defer`被设置时,脚本会在整个HTML文档解析完毕(即DOMContentLoaded事件触发)但`window.onload`事件触发之前执行,按照脚本在...

Global site tag (gtag.js) - Google Analytics