Javascript 加載性能優化

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

下載在有些瀏覽器中是並行的,有些瀏覽器中是串行的,如IE八、Firefox三、Chrome2都是串行下載的css

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

阻塞特性:

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

1、腳本位置

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

<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文件所有下載執行完成後,頁面纔會繼續渲染,把腳本放在頁面頂部會致使明顯延遲,一般表現爲顯示空白頁,用戶沒法瀏覽內容,也沒法與頁面交互。github

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

因爲腳本會阻塞頁面其它資源的下載,所以推薦將javasrcipt儘可能放到body標籤的底部,以減小對整個頁面下載的影響。數組

2、組織腳本

因爲<script>標籤在下載時會阻塞頁面的渲染,因此減小<script>標籤數量有助於改善這一狀況。建議將多個javascript文件合併爲一個,這樣能夠減小性能的消耗。同時也能夠減小請求的數量。瀏覽器

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

3、無阻塞腳本

一、延遲腳本

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。

二、動態腳本元素

<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連接除外)。

三、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下載,所以不適合大型網站或項目。

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

一、YUI3的方式

二、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

三、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/

四、SeaJS(7.5k)

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

五、do 框架(3.5k)

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

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

六、RequireJS(13.1k)

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

相關文章
相關標籤/搜索