[翻譯]High Performance JavaScript(004)

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代碼的網頁應用的實際性能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章