頁面生命周期——DOMContentLoaded&Loaded

頁面生命周期內,有兩個非常重要的事件:
1.DOMContentLoaded
此時瀏覽器已經完全加載了HTML文件,并且DOM樹已經生成好了。但是其他外部資源,如樣式文件、圖片、字體等并沒有加載好
2.Load
此時瀏覽器已經將所有的資源都加載完畢,可以正確讀取頁面中的資源。

兩個事件標識了兩種不同的時刻:
1.DOMContentLoaded
DOM已經完成加載,此時可以為這些DOM元素綁定事件,初始化接口等
2.Load
其他外部資源均已加載完成,可以正確讀出這些資源的信息,如圖片的寬高等

DOMContentLoaded

使用addEventListener監聽該事件:

document.addEventListener('DOMContentLoaded', ready)

舉一個??

<script>
  function ready() {
    alert('DOM is ready');
    
    var img = document.getElementById('img')
    // 由于圖片屬于外部資源,尚未下載完成,因此暫無法讀到數據,所以結果是0x0
    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
  }

  document.addEventListener("DOMContentLoaded", ready);
</script>

![](http://upload-images.jianshu.io/upload_images/280956-d030e8153b43d7be.gif?imageMogr2/auto-orient/strip)

雖然DOMContentLoaded如我們期望的那樣工作,但仍在個別情況下有所不同

異步scripts

當解析器(HTML Paser)讀取到<script>...</script>內聯標簽時,會阻塞DOM構建,它會立即執行腳本,原因是可能這些腳本會影響DOM,因此需要等到這些腳本都執行完成了,才會觸發DOMContentLoaded事件。
外部腳本(<script src="...">...</script>)也是如此,瀏覽器需要等到加載后再執行完成才可以繼續執行。

是否可以讓外部引用的腳本延后執行,答案是可以的。asyncdefer屬性可以讓外部腳本延后執行,而不阻塞瀏覽器解析文檔。asyncdefer僅在<script src="...">...</script>情形下起作用,對內聯腳本是無效的。用戶可以在腳本完成加載前,就可以看到頁面了,提升了用戶體驗。

async & defer

這兩個屬性都是告訴瀏覽器,標記的腳本不需要等待它加載完成,瀏覽器可以繼續完成DOM構建和渲染
兩者的區別如下:

async defer
執行順序 誰先完成下載誰先執行 執行順序始終遵循加載的順序,即使先下載完成了
DOMContentLoaded 如果頁面加載時間較長,腳本可能會先執行;絕大多數會在DOMContentLoaded之后執行,但一定是在Load事件之前執行 腳本會在DOMContentLoaded之前執行,但不阻塞瀏覽器加載和解析文檔,即defer腳本與瀏覽器加載順序無關先后

因此async更符合一些應用場景。

Timeline

樣式阻塞

對于外部樣式文件而言,并不會影響到DOMCotentLoaded事件,它并不會等待外部樣式文件加載完成。
但是!?。?,如果一個腳本是緊跟著link樣式出現,那么這個腳本就會等樣式加載完成了。如下:

<link type="text/css" rel="stylesheet" href="style.css">
<script>
  // the script doesn't not execute until the stylesheet is loaded
  alert(getComputedStyle(document.body).marginTop);
</script>

原因是瀏覽器猜測腳本可能會讀取一些樣式信息,如位置、顏色,顯然腳本就需要等到樣式的加載完成了。

這也是為什么我們最好把腳本文件放到HTML文檔的最后再加載,而不要把腳本放在head標簽里,除非你清楚放在head標簽里是必要的。

瀏覽器表單自動填充

這個是順便引出的話題,你知道瀏覽器內置的表單自動填充在什么時候完成么?沒錯,就是DOMContentLoaded觸發的時候

window.onload

當所有外部資源都完成加載后,瀏覽器觸發load事件,此時可以讀取外部資源信息了

<script>
  window.onload = function() {
    alert('Page loaded');

    // image is loaded at this time
    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);
  };
</script>

![](http://upload-images.jianshu.io/upload_images/280956-d030e8153b43d7be.gif?imageMogr2/auto-orient/strip)

Chrome DevTool中的load

image.png

我們通??梢酝ㄟ^DOMContentLoaded的時間去衡量一個頁面加載的速度,因為此時用戶已經可以看到這個頁面,并可以有一些交互了。

兼容性

以上提到的使用document.addEventListener監聽DOMCotentLoaded事件,在IE9+都是有效的。需要兼容IE低版本

// 摘抄自jQuery源碼
if ( document.readyState === "complete" ||
    ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
    // Handle it asynchronously to allow scripts the opportunity to delay ready
    window.setTimeout( jQuery.ready );
}

比較粗暴,反復去嗅探是否可以執行ready函數。有興趣去讀讀jQuery源碼,很有意思

擴展閱讀

DOMContentLoaded
onload
page lifecycle

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一:在制作一個Web應用或Web站點的過程中,你是如何考慮他的UI、安全性、高性能、SEO、可維護性以及技術因素的...
    Arno_z閱讀 1,212評論 0 1
  • 1.幾種基本數據類型?復雜數據類型?值類型和引用數據類型?堆棧數據結構? 基本數據類型:Undefined、Nul...
    極樂君閱讀 5,594評論 0 106
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,368評論 25 708
  • 還沒好好地感受盛夏,卻已入秋.廈門的秋沒有“滿地黃花堆積”.自然感受不到“這次第,怎一個愁字了得”.來廈快十...
    IVI_Y閱讀 318評論 3 1
  • 金思米/文 揭露微信投票中獎之謎 一件事情,不同的眼睛,看法截然不同。同樣的事物,不同人的...
    金思米閱讀 1,134評論 5 4