非阻塞式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()函数加载页面所需的额外功能函数。

时间: 2024-11-06 09:54:12

非阻塞式JavaScript脚本及延伸知识的相关文章

非阻塞式JavaScript脚本介绍

JavaScript 倾向于阻塞浏览器某些处理过程,如HTTP 请求和界面刷新,这是开发者面临的最显著的性能问题.保持JavaScript文件短小,并限制HTTP请求的数量,只是创建反应迅速的网页应用的第一步.一个应用程序所包含的功能越多,所需要的JavaScript 代码就越大,保持源码短小并不总是一种选择.尽管下载一个大JavaScript 文件只产生一次HTTP 请求,却会锁定浏览器一大段时间.为避开这种情况,你需要向页面中逐步添加JavaScript,某种程度上说不会阻塞浏览器.非阻塞脚

JavaScript非阻塞加载脚本

As more and more sites evolve into “Web 2.0″ apps, the amount of JavaScript increases. This is a performance concern because scripts have a negative impact on page performance. Mainstream browsers (i.e., IE 6 and 7)  block in two ways: Resources in t

Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别

1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已. 2. 异步: 所谓异步,"调用"在发出之后,这个调用就直接返回了,所以没有返回结果. 换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果.而是在"调用"发出后,"被调用者&q

Java NIO实现非阻塞式socket通信

博主知识水平有限,只能提供一个个人的狭隘的理解,如果有新人读到这儿,建议看一下其他教程或者API,如果不明白,再来看一下:如果有dalao读到这儿,希望能指出理解中的问题~谢谢 Java提供了用于网络通信的socket和serversocket包,然而实现方式是阻塞式的,同一时间点上只能进行一个连接,这会带来不好的体验.当然了,我们也可以通过不断创建线程的方式管理连接,但线程多了的话反而会降低效率.于是Java推出了非阻塞式IO--channel.并且channel提供关于网络通信的相关chan

非入侵式JavaScript

非入侵式JavaScript针对哪些情况: 1.并不是每个人的浏览器都支持JavaScript.需要让每个人都能看到全部内容,并且无须在浏览器中执行代码 就能使用该应用. 2.一些运行方式非常奇怪的浏览器.比如视觉受损的人会使用屏幕阅读器,一些手机用户无法使用含有 JavaScript的站点. 3.JavaScript在不同的平台上运行方式不同,IE是造成这一问题的罪魁祸首.需要根据不用的浏览器编写不同 的事件处理代码. 4.这些事件处理器都会引用全局命名空间中的函数.如果想把其他类库集成进来,

Java基础:非阻塞式IO

转载请注明出处:jiq?钦's technical Blog 引言 JDK1.4中引入了NIO,即New IO,目的在于提高IO速度.特别注意JavaNIO不完全是非阻塞式IO(No-Blocking IO),因为其中部分通道(如FileChannel)只能运行在阻塞模式下,而其他的通道可以在阻塞式和非阻塞式之间进行选择. 尽管这样,我们还是习惯将Java NIO看作是非阻塞式IO,而前面介绍的面向流(字节/字符)的IO类库则是非阻塞的,详细来看,两者区别如下: IO NIO 面向流(Strea

前段时间用java做了个非阻塞式仿飞秋聊天程序

采用Swing 布局 NIO非阻塞式仿飞秋聊天程序, 切换皮肤颜色什么的小功能以后慢慢做 启动主程序. 当用户打开主程序后自动获取局域网段IP可以在 设置 --> IP网段过滤, 拥有 JMF 视频聊天功能(取得视频流读取到ByteBuffer然后写入DatagramChannel), 其实什么功能都是可以加的后期, 简单介绍下 双击用户进行聊天 (第一版是基于channel通道的操作, 非阻塞式, 这一版本为阻塞式) 当有消息发来时自动开启一个线程处理客户端请求并接受数据,并在标题上提示收到新

4.NIO的非阻塞式网络通信

/*阻塞 和 非阻塞 是对于 网络通信而言的*/ /*原先IO通信在进行一些读写操作 或者 等待 客户机连接 这种,是阻塞的,必须要等到有数据被处理,当前线程才被释放*/ /*NIO 通信 是将这个阻塞的过程 丢给了选择器,客户端和 服务器端 之间建立的通道,都会注册到 选择器上,然后用选择器 实时监控 我们这些通道上的状况*/ /*当某一个通道上 某一个请求的事件 完全准备就绪时,那么选择器才会将 这个任务 分配到服务器上的一个 或多个线程中*/ /*阻塞 与 非阻塞*/ 传统的IO 流都是

C语言非阻塞式键盘监听

监听键盘可以使用C语言的字符输入函数,例如 getchar.getch.getche 等,我们会在<结合缓冲区谈谈C语言getchar().getche().getch()的区别>一节中重点讲解它们的区别. 使用getche函数监听键盘的例子: #include <stdio.h> #include <conio.h> int main(){ char ch; int i = 0; //循环监听,直到按Esc键退出 while(ch = getch()){ if(ch