Javascript 加載性能優化

jopen 12年前發布 | 3K 次閱讀 web開發人員

瀏覽器對javascript的處理主要有2部分:下載和執行

下載在有些瀏覽器中是并行的,有些瀏覽器中是串行的,如IE8、Firefox3、Chrome2都是串行下載的

執行在所有瀏覽器中默認都是阻塞的,當js在執行時不會進行html解析等其它操作

阻塞特性:

javascript有個阻塞特性,當瀏覽器執行javascript代碼時,不能同時做其它任何事情。無論當前javascript代碼是內嵌還 是在外鏈文件中,頁面的下載和渲染都必須停下來等待腳本執行完成。瀏覽器在下載和執行腳本是進出現阻塞的原因在于,腳本可能會改變頁面或 javascript的命名空間,它們對后面頁面內容造成影響。

一、腳本位置

瀏覽器在碰到一個引入外部javascript文件的<script>標簽時會停下所有工作來下載并解析執行它,在這個過程中,頁面渲染和用戶交互完全被阻塞了。例:

<html>  
<head>  
    <title>無標題文檔</title>  
    <link rel="stylesheet" type="text/css" href="styles.css" />
    <script type="text/javascript" src="file1.js"></script>  
    <script type="text/javascript" src="file2.js"></script>  
    <script type="text/javascript" src="file3.js"></script>  
</body> 
</head>  
<body>  
    <p>頁面的內容。。。</p>
</body>
</html>

由于腳本的阻塞特性,頁面會在3個javascript文件全部下載執行完成后,頁面才會繼續渲染,把腳本放在頁面頂部會導致明顯延遲,通常表現為顯示空白頁,用戶無法瀏覽內容,也無法與頁面交互。

ie8+、firefox 3.5+、safari4+、chrome2+都允許并行下載javascript文件,但是在下載的過程中仍然會阻塞圖片等其它資源的下載。

由于腳本會阻塞頁面其它資源的下載,因此推薦將javasrcipt盡量放到body標簽的底部,以減少對整個頁面下載的影響。

二、組織腳本

由于<script>標簽在下載時會阻塞頁面的渲染,所以減少<script>標簽數量有助于改善這一情況。建議將多個javascript文件合并為一個,這樣可以減少性能的消耗。同時也可以減少請求的數量。

(參考:在服務端合并和壓縮javascript和CSS文件

三、無阻塞腳本

1、延遲腳本

HTML4 為<script>標簽定義了一個defer 屬性,它能使這段代碼延遲執行,然而該屬性只有IE4+支持,因此它不是一個理想的跨瀏覽器解決方案。聲明了defer 屬性的script會在DOM加載完成,window.onload 事件觸發前被解析執行:

<html>  
<head>  
    <title>script defer example</title>  
</body> 
</head>  
<body>
<script defer>
    alert('defer');
</script>
<script>
    alert('script');
</script>
<script>
    window.onload = function(){
        alert('load');
    }
</script>
</body>
</html>

這段代碼在支持defer屬性的瀏覽器彈出順序是:script、defer、load;不支持defer屬性的瀏覽器彈出的順序是defer、script、load。

2、動態腳本元素

<script type="text/javascript">
function loadScript(url, callback) {
    var script = document.createElement('script')
    script.type = 'text/javascript';

    if (script.readyState) { //for ie
        script.onreadystatechange = function() {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
                script.onreadystatechange = null;
                callback();
            }
        };
    } else { //other browser
        script.onload = function() {
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
}
</script>

loadscript函數用法

<script type="text/javascript">
    //單個文件
    loadScript('file1.js', function(){
        alert('loaded!');
    });


    //多個文件
    loadScript('file1.js', function(){
        loadScript('file2.js',function(){
            loadScript('file3.js', function(){
                alert('all files loaded!');
            });
        });
    });
</script>    

這種技術的重點在于:無論何時啟動下載,文件的下載和執行過程不會阻塞頁面其它進程,你甚至可以將代碼放在頁面的head區域而不影響頁面的其它部分(下載該文件的http鏈接除外)。

3、XMLHttpRequest 腳本注入

此技術會先創建一個XHR對象,然后用它下載javascript文件,最后創建動態的script元素將代碼注入到頁面中。

<script type="text/javascript">
var xhr = new XMLHttpRequest();
xhr.open('get', 'file1.js', true);
xhr.onreadystatechange = function() {
    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);
</script>

這種方法優點是可以直接下載javascript代碼但不立即執行。由于代碼是在<script>標簽之外返回的,因此下載后不會自動 執行,這使得是可以把腳本推遲到你準備好的時候。這種方法的局限性在于javascript文件必須與所請求的頁面處于相同的域,這意味著 javascript文件不能從cdn下載,因此不適合大型網站或項目。

四、推薦的無阻塞加載方式

1、YUI3的方式

2、LazyLoad(1.5k)

Yahoo!Search工程師Ryan Grove創建的一個通用的延遲加載工具,是loadScript()函數的增強版。

用法示例:

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

LazyLoad同樣支持多個javascript文件,并能保證在所有瀏覽器中都可以按正確的順序執行。要加載多個javscript文件,只需要給LazyLoad.js()y方法傳入一個url數組:

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

項目地址:https://github.com/rgrove/lazyload

3、LABjs(4.7k)

LABjs是Kyle Simpson受Steve Sounders的啟發實現的無阻塞加載工具。用法示例:

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

$LAB.script()方法用來定義需要下載的javascript文件,$LAB.wait()用來指定文件下載并執行完畢后所調用的函數。

要下載多個javscript文件,只需鏈式調用另一個$LAB.script()方法:

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

LABjs與眾不同的是它管理依賴關系的能力。通常來說,連續的<script>標簽意味著文件逐個下載并按順序執行。

LABjs允許使用wait()方法來指定哪些文件需要等待其它文件。上面的例子中first.js不能保證會在the-reset.js的代碼前執行,為了確保這一點,必須在第一個script()方法后調用wait():

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

項目地址:hhttp://labjs.com/

4、SeaJS(7.5k)

SeaJS 是淘寶玉伯開發的一個遵循 CommonJS 規范的模塊加載框架,可用來輕松愉悅地加載任意 javascript 模塊。詳細請參考:http://seajs.com/docs/

5、do 框架(3.5k)

Do是豆瓣網kejun開發的一個很輕量的Javascript開發框架。目前do.min.js。它的核心功能是對模塊進行組織和加載。加載采取并行異步隊列的策略,并且可以控制執行時機。Do可以任意置換核心類庫,默認是jQuery。

項目地址:https://github.com/kejun/Do

6、RequireJS(13.1k)

項目地址:http://requirejs.org/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!