1. 程式人生 > >【轉載】DOMContentLoaded與load的區別

【轉載】DOMContentLoaded與load的區別

頭部 布局 chrome 顯示 err event scrip rom 資源文件

技術分享圖片

(1)在chrome瀏覽器的開發過程中,我們會看到network面板中有這兩個數值,分別對應網 絡請求上的標誌線,這兩個時間數值分別代表什麽?

(2)我們一再強調將css放在頭部,將js文件放在尾部,這樣有利於優化頁面的性能,為什麽這種方式能夠優化性能?

(3)在用jquery的時候,我們一般都會將函數調用寫在ready方法內,這是什麽原理?

首先看一下

DOMContentLoaded顧名思義,就是dom內容加載完畢。那什麽是dom內容加載完畢呢?我們從打開一個網頁說起。當輸入一個URL,頁面的展示首先是空白的,然後過一會,頁面會展示出內容,但是頁面的有些資源比如說圖片資源還無法看到,此時頁面是可以正常的交互,過一段時間後,圖片才完成顯示在頁面。從頁面空白到展示出頁面內容,會觸發DOMContentLoaded事件。而這段時間就是HTML文檔被加載和解析完成。

這時候問題又來了,什麽是HTML文檔被加載和解析完成。要解決這個問題,我們就必須了解瀏覽器渲染原理。

當我們在瀏覽器地址輸入URL時,瀏覽器會發送請求到服務器,服務器將請求的HTML文檔發送回瀏覽器,瀏覽器將文檔下載下來後,便開始從上到下解析,解析完成之後,會生成DOM。如果頁面中有css,會根據css的內容形成CSSOM,然後DOM和CSSOM會生成一個渲染樹,最後瀏覽器會根據渲染樹的內容計算出各個節點在頁面中的確切大小和位置,並將其繪制在瀏覽器上。

技術分享圖片

下面就是頁面加載和解析過程中,瀏覽器的一個快照

技術分享圖片

上面我們看到在解析html的過程中,html的解析會被中斷,這是因為javascript會阻塞dom的解析。當解析過程中遇到<script>標簽的時候,便會停止解析過程,轉而去處理腳本,如果腳本是內聯的,瀏覽器會先去執行這段內聯的腳本,如果是外鏈的,那麽先會去加載腳本,然後執行。在處理完腳本之後,瀏覽器便繼續解析HTML文檔。

同時javascript的執行會受到標簽前面樣式文件的影響。如果在標簽前面有樣式文件,需要樣式文件加載並解析完畢後才執行腳本。這是因為javascript可以查詢對象的樣式。

這裏需要註意一點,在現在瀏覽器中,為了減緩渲染被阻塞的情況,現代的瀏覽器都使用了猜測預加載。當解析被阻塞的時候,瀏覽器會有一個輕量級的HTML(或CSS)掃描器(scanner)繼續在文檔中掃描,查找那些將來可能能夠用到的資源文件的url,在渲染器使用它們之前將其下載下來。

在這裏我們可以明確DOMContentLoaded所計算的時間,當文檔中沒有腳本時,瀏覽器解析完文檔便能觸發 DOMContentLoaded 事件;如果文檔中包含腳本,則腳本會阻塞文檔的解析,而腳本需要等位於腳本前面的css加載完才能執行。在任何情況下,DOMContentLoaded 的觸發不需要等待圖片等其他資源加載完成。

接下來,我們來說說load,頁面上所有的資源(圖片,音頻,視頻等)被加載以後才會觸發load事件,簡單來說,頁面的load事件會在DOMContentLoaded被觸發之後才觸發。

我們在 jQuery 中經常使用的 $(document).ready(function() { // ...代碼... }); 其實監聽的就是 DOMContentLoaded 事件,而$(document).load(function() { // ...代碼... }); 監聽的是 load 事件。在用jquery的時候,我們一般都會將函數調用寫在ready方法內,就是頁面被解析後,我們就可以訪問整個頁面的所有dom元素,可以縮短頁面的可交互時間,提高整個頁面的體驗。

下面我們在來看看如何實現這兩個函數

1、onload事件

onload事件所有的瀏覽器都支持,所以我們不需要什麽兼容,只要通過調用

window.onload = function(){
    
}

2、DOMContentLoaded 事件

DOMContentLoaded不同的瀏覽器對其支持不同,所以在實現的時候我們需要做不同瀏覽器的兼容。

1)支持DOMContentLoaded事件的,就使用DOMContentLoaded事件;

2)IE6、IE7不支持DOMContentLoaded,但它支持onreadystatechange事件,該事件的目的是提供與文檔或元素的加載狀態有關的信息。

3) 更低的ie還有個特有的方法doScroll, 通過間隔調用:document.documentElement.doScroll("left");

可以檢測DOM是否加載完成。 當頁面未加載完成時,該方法會報錯,直到doScroll不再報錯時,就代表DOM加載完成了。該方法更接近DOMContentLoaded的實現。

技術分享圖片
function ready(fn){

    if(document.addEventListener) {
        document.addEventListener(‘DOMContentLoaded‘, function() {
            document.removeEventListener(‘DOMContentLoaded‘,arguments.callee, false);
            fn();
        }, false);
    } 

    // 如果IE
    else if(document.attachEvent) {
        // 確保當頁面是在iframe中加載時,事件依舊會被安全觸發
        document.attachEvent(‘onreadystatechange‘, function() {
            if(document.readyState == ‘complete‘) {
                document.detachEvent(‘onreadystatechange‘, arguments.callee);
                fn();
            }
        });

        // 如果是IE且頁面不在iframe中時,輪詢調用doScroll 方法檢測DOM是否加載完畢
        if(document.documentElement.doScroll && typeof window.frameElement === "undefined") {
            try{
                document.documentElement.doScroll(‘left‘);
            }
            catch(error){
                return setTimeout(arguments.callee, 20);
            };
            fn();
        }
    }
};
技術分享圖片

最後我們來回答這個問題:我們為什麽一再強調將css放在頭部,將js文件放在尾部

在面試的過程中,經常會有人在回答頁面的優化中提到將js放到body標簽底部,原因是因為瀏覽器生成Dom樹的時候是一行一行讀HTML代碼的,script標簽放在最後面就不會影響前面的頁面的渲染。那麽問題來了,既然Dom樹完全生成好後頁面才能渲染出來,瀏覽器又必須讀完全部HTML才能生成完整的Dom樹,script標簽不放在body底部是不是也一樣,因為dom樹的生成需要整個文檔解析完畢。

技術分享圖片

我們再來看一下chrome在頁面渲染過程中的,綠色標誌線是First Paint的時間。納尼,為什麽會出現firstpaint,頁面的paint不是在渲染樹生成之後嗎?其實現代瀏覽器為了更好的用戶體驗,渲染引擎將嘗試盡快在屏幕上顯示的內容。它不會等到所有HTML解析之前開始構建和布局渲染樹。部分的內容將被解析並顯示。也就是說瀏覽器能夠渲染不完整的dom樹和cssom,盡快的減少白屏的時間。假如我們將js放在header,js將阻塞解析dom,dom的內容會影響到First Paint,導致First Paint延後。所以說我們會將js放在後面,以減少First Paint的時間,但是不會減少DOMContentLoaded被觸發的時間。

【轉載】DOMContentLoaded與load的區別