[翻译]High Performance JavaScript(004)

来源:互联网 发布:联想电池修复软件 编辑:程序博客网 时间:2024/05/16 14:32

XMLHttpRequest Script Injection  XHR脚本注入

 

    Another approach to nonblocking scripts is to retrieve the JavaScript code using an XMLHttpRequest (XHR) object and then inject the script into the page. This technique involves creating an XHR object, downloading the JavaScript file, then injecting the JavaScript code into the page using a dynamic <script> element. Here's a simple example:

    另一个以非阻塞方式获得脚本的方法是使用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_x("script");
      script.type = "text/javascript";
      script.text = xhr.responseText;
      document.body.appendChild(script);
    }
  }
};
xhr.send(null);

    This code sends a GET request for the file file1.js. The onreadystatechange event handler checks for a readyState of 4 and then verifies that the HTTP status code is valid (anything in the 200 range means a valid response, and 304 means a cached response). If a valid response has been received, then a new <script> element is created and its text property is assigned to the responseText received from the server. Doing so essentially creates a <script> element with inline code. Once the new <script> element is added to the document, the code is executed and is ready to use.

    此代码向服务器发送一个获取file1.js文件的GET请求。onreadystatechange事件处理函数检查readyState是不是4,然后检查HTTP状态码是不是有效(2XX表示有效的回应,304表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接收到的responseText字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新<script>元素被添加到文档,代码将被执行,并准备使用。

    The primary advantage of this approach is that you can download the JavaScript code without executing it immediately. Since the code is being returned outside of a <script> tag, it won't automatically be executed upon download, allowing you to defer its execution until you're ready. Another advantage is that the same code works in all modern browsers without exception cases.

    这种方法的主要优点是,你可以下载不立即执行的JavaScript代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得你可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。

    The primary limitation of this approach is that the JavaScript file must be located on the same domain as the page requesting it, which makes downloading from CDNs impossible. For this reason, XHR script injection typically isn't used on large-scale web applications.

    此方法最主要的限制是:JavaScript文件必须与页面放置在同一个域内,不能从CDNs下载(CDN指“内容投递网络(Content Delivery Network)”,前面002篇《成组脚本》一节提到)。正因为这个原因,大型网页通常不采用XHR脚本注入技术。


Recommended Nonblocking Pattern  推荐的非阻塞模式

 

    The recommend approach to loading a significant amount of JavaScript onto a page is a two-step process: first, include the code necessary to dynamically load JavaScript, and then load the rest of the JavaScript code needed for page initialization. Since the first part of the code is as small as possible, potentially containing just the loadScript() function, it downloads and executes quickly, and so shouldn't cause much interference with the page. Once the initial code is in place, use it to load the remaining JavaScript. For example:

    推荐的向页面加载大量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>

    Place this loading code just before the closing </body> tag. Doing so has several benefits. First, as discussed earlier, this ensures that JavaScript execution won't prevent the rest of the page from being displayed. Second, when the second JavaScript file has finished downloading, all of the DOM necessary for the application has been created and is ready to be interacted with, avoiding the need to check for another event (such as window.onload) to know when the page is ready for initialization.

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

    Another option is to embed the loadScript() function directly into the page, thus avoiding another HTTP request. For example:

    另一个选择是直接将loadScript()函数嵌入在页面中,这可以避免另一次HTTP请求。例如:

<script type="text/javascript">
  function loadScript(url, callback){
    var script = document.createElement_x("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);
  }
  loadScript("the-rest.js", function(){
    Application.init();
  });
</script>

    If you decide to take the latter approach, it's recommended to minify the initial script using a tool such as YUI Compressor (see Chapter 9) for the smallest byte-size impact on your page.

    如果你决定使用这种方法,建议你使用“YUI Compressor”(参见第9章)或者类似的工具将初始化脚本缩小到最小字节尺寸。

    Once the code for page initialization has been completely downloaded, you are free to continue using loadScript() to load additional functionality onto the page as needed.

    一旦页面初始化代码下载完成,你还可以使用loadScript()函数加载页面所需的额外功能函数。

 

The YUI 3 approach

    The concept of a small initial amount of code on the page followed by downloading additional functionality is at the core of the YUI 3 design. To use YUI 3 on your page, begin by including the YUI seed file:

    YUI 3的核心设计理念为:用一个很小的初始代码,下载其余的功能代码。要在页面上使用YUI 3,首先包含YUI的种子文件:

<script type="text/javascript"

src=http://yui.yahooapis.com/combo?3.0.0/build/yui/yui-min.js></script>

    The seed file is around 10 KB (6 KB gzipped) and includes enough functionality to download any other YUI components from the Yahoo! CDN. For example, if you'd like to use the DOM utility, you specify its name ("dom") with the YUI use() method and then provide a callback that will be executed when the code is ready:

    此种子文件大约10KB(gzipped压缩后6KB)包含从Yahoo! CDN下载YUI组件所需的足够功能。举例来说,如果你想使用DOM功能,你可以指出它的名字("dom"),传递给YUI的use()函数,再提供一个回调函数,当代码准备好时这个回调函数将被调用:

YUI().use("dom", function(Y){
  Y.DOM.addClass(docment.body, "loaded");
});

    This example creates a new instance of the YUI object and then calls the use() method. The seed file has all of the information about filenames and dependencies, so specifying "dom" actually builds up a combo-handler URL with all of the correct dependency files and creates a dynamic script element to download and execute those files. When all of the code is available, the callback method is called and the YUI instance is passed in as the argument, allowing you to immediately start using the newly downloaded functionality.

    这个例子创建了一个新的YUI实例,然后调用use()函数。种子文件拥有关于文件名和依赖关系的所有信息,所以指定“dom”实际上建立了一个由正确的依赖文件所组成的“联合句柄”URL,并创建一个动态脚本元素下载并执行这些文件。当所有代码可用时,回调函数被调用,YUI实例将作为参数传入,使你可以立即使用新下载的功能。

 

The LazyLoad library

    For a more general-purpose tool, Ryan Grove of Yahoo! Search created the LazyLoad library (available at http://github.com/rgrove/lazyload/). LazyLoad is a more powerful version of the loadScript() function. When minified, the LazyLoad file is around 1.5 KB (minified, not gzipped). Example usage:

    作为一个更通用的工具,Yahoo! Search的Ryan Grove创建了LazyLoad库(参见http://github.com/rgrove/lazyload/)。LazyLoad是一个更强大的loadScript()函数。LazyLoad精缩之后只有大约1.5KB(精缩,而不是用gzip压缩的)。用法举例如下:

<script type="text/javascript" src="lazyload-min.js"></script>
<script type="text/javascript">
  LazyLoad.js("the-rest.js", function(){
    Application.init();
  });
</script>

    LazyLoad is also capable of downloading multiple JavaScript files and ensuring that they are executed in the correct order in all browsers. To load multiple JavaScript files, just pass an array of URLs to the LazyLoad.js() method:

    LazyLoad还可以下载多个JavaScript文件,并保证它们在所有浏览器上都能够按照正确的顺序执行。要加载多个JavaScript文件,只要调用LazyLoad.js()函数并传递一个URL队列给它:

<script type="text/javascript" src="lazyload-min.js"></script>
<script type="text/javascript">
  LazyLoad.js(["first-file.js", "the-rest.js"], function(){
    Application.init();
  });
</script>

    Even though the files are downloaded in a nonblocking fashion using dynamic script loading, it's recommended to have as few files as possible. Each download is still a separate HTTP request, and the callback function won't execute until all of the files have been downloaded and executed.

    即使这些文件是在一个非阻塞的方式下使用动态脚本加载,它建议应尽可能减少文件数量。每次下载仍然是一个单独的HTTP请求,回调函数直到所有文件下载并执行完之后才会运行。

 

The LABjs library

    Another take on nonblocking JavaScript loading is LABjs (http://labjs.com/), an open source library written by Kyle Simpson with input from Steve Souders. This library provides more fine-grained control over the loading process and tries to download as much code in parallel as possible. LABjs is also quite small, 4.5 KB (minified, not gzipped), and so has a minimal page footprint. Example usage:

    另一个非阻塞JavaScript加载库是LABjs(http://labjs.com/),Kyle Simpson写的一个开源库,由Steve Souders赞助。此库对加载过程进行更精细的控制,并尝试并行下载尽可能多的代码。LABjs也相当小,只有4.50KB(精缩,而不是用gzip压缩的),所以具有最小的页面代码尺寸。用法举例:

<script type="text/javascript" src="lab.js"></script>
<script type="text/javascript">
  $LAB.script("the-rest.js")
      .wait(function(){
        Application.init();
      });
</script>

    The $LAB.script() method is used to define a JavaScript file to download, whereas $LAB.wait() is used to indicate that execution should wait until the file is downloaded and executed before running the given function. LABjs encourages chaining, so every method returns a reference to the $LAB object. To download multiple JavaScript files, just chain another $LAB.script() call:

    $LAB.script()函数用于下载一个JavaScript文件,$LAB.wait()函数用于指出一个函数,该函数等待文件下载完成并运行之后才会被调用。LABjs鼓励链操作,每个函数返回一个指向$LAB对象的引用。要下载多个JavaScript文件,那么就链入另一个$LAB.script()调用,如下:

<script type="text/javascript" src="lab.js"></script>
<script type="text/javascript">
  $LAB.script("first-file.js")
      .script("the-rest.js")
      .wait(function(){
        Application.init();
      });
</script>

    What sets LABjs apart is its ability to manage dependencies. Normal inclusion with <script> tags means that each file is downloaded (either sequentially or in parallel, as mentioned previously) and then executed sequentially. In some cases this is truly necessary, but in others it is not.

    LABjs的独特之处在于它能够管理依赖关系。一般来说<script>标签意味着每个文件下载(或按顺序,或并行,如前所述),然后按顺序执行。在某些情况下这非常必要,但有时未必。

    LABjs allows you to specify which files should wait for others by using wait(). In the previous example, the code in first-file.js is not guaranteed to execute before the code in the-rest.js. To guarantee this, you must add a wait() call after the first script():

    LABjs通过wait()函数允许你指定哪些文件应该等待其他文件。在前面的例子中,first-file.js的代码不保证在the-rest.js之前运行。为保证这一点,你必须在第一个script()函数之后添加一个wait()调用:

<script type="text/javascript" src="lab.js"></script>
<script type="text/javascript">
  $LAB.script("first-file.js").wait()
      .script("the-rest.js")
      .wait(function(){
        Application.init();
      });
</script>

    Now the code in first-file.js is guaranteed to execute before the code in the-rest.js, although the contents of the files are downloaded in parallel.

    现在,first-file.js的代码保证会在the-rest.js之前执行,虽然两个文件的内容是并行下载的。

 

Summary  总结

 

    Managing JavaScript in the browser is tricky because code execution blocks other browser processes such as UI painting. Every time a <script> tag is encountered, the page must stop and wait for the code to download (if external) and execute before continuing to process the rest of the page. There are, however, several ways to minimize the performance impact of JavaScript:

    管理浏览器中的JavaScript代码是个棘手的问题,因为代码执行阻塞了其他浏览器处理过程,诸如用户界面绘制。每次遇到<script>标签,页面必须停下来等待代码下载(如果是外部的)并执行,然后再继续处理页面其他部分。但是,有几种方法可以减少JavaScript对性能的影响:


• Put all <script> tags at the bottom of the page, just inside of the closing </body> tag. This ensures that the page can be almost completely rendered before script execution begins.

    将所有<script>标签放置在页面的底部,紧靠body关闭标签</body>的上方。此法可以保证页面在脚本运行之前完成解析。


• Group scripts together. The fewer <script> tags on the page, the faster the page can be loaded and become interactive. This holds true both for <script> tags loading external JavaScript files and those with inline code.

    将脚本成组打包。页面的<script>标签越少,页面的加载速度就越快,响应也更加迅速。不论外部脚本文件还是内联代码都是如此。


• There are several ways to download JavaScript in a nonblocking fashion:
— Use the defer attribute of the <script> tag (Internet Explorer and Firefox 3.5+ only)
— Dynamically create <script> elements to download and execute the code
— Download the JavaScript code using an XHR object, and then inject the code into the page

    有几种方法可以使用非阻塞方式下载JavaScript:

——为<script>标签添加defer属性(只适用于Internet Explorer和Firefox 3.5以上版本)

——动态创建<script>元素,用它下载并执行代码

——用XHR对象下载代码,并注入到页面中


    By using these strategies, you can greatly improve the perceived performance of a web application that requires a large amount of JavaScript code.

    通过使用上述策略,你可以极大提高那些大量使用JavaScript代码的网页应用的实际性能。

原创粉丝点击