天天看點

domReady的了解

domReady的了解

domReady

是名為

DOMContentLoaded

事件的别稱,當初始的

HTML

文檔被完全加載和解析完成之後,

DOMContentLoaded

事件被觸發,而無需等待樣式表、圖像和子架構的完全加載。

描述

浏覽器渲染

DOM

結構是有一定順序的,雖然不同浏覽器的實作各有不同,但是基本流程都大緻相同:

  • 自上而下,首先解析

    HTML

    标簽,生成

    DOM Tree

  • 在解析到

    <link>

    或者

    <style>

    标簽時,開始解析

    CSS

    ,生成

    CSSOM

    ,值的注意的是此時解析

    HTML

    标簽與解析

    CSS

    是并行執行的。
  • 當遇到

    <script>

    标簽後,浏覽器會立即開始解析腳本,并停止解析文檔,因為腳本有可能會改動

    DOM

    CSS

    ,繼續解析會浪費資源,是以應當将

    <script>

    标簽放于

    <body></body>

    後。
  • DOM Tree

    CSSOM

    生成後,将兩者結合進行布局,計算它們的大小位置等布局資訊,形成一個能夠表示這所有資訊的内部表示模型,可稱為渲染樹

    render tree

  • 根據計算好的資訊繪制整個頁面,系統會周遊渲染樹,并調用

    paint

    方法,将内容顯示在螢幕上。

在浏覽器解析

DOM

結構的過程中是存在阻塞過程的:

  • 解析

    JavaScript

    過程中會阻塞浏覽器的解析過程,準确來說解析渲染過程與解析

    JavaScript

    的過程是互斥的。
  • CSS

    加載解析時不會阻塞

    DOM

    樹的解析過程,這兩個解析過程是可以并行的,但是

    CSS

    加載過程中是不能進行

    JavaScript

    的解析的,也就是說

    CSS

    加載過程中是會阻塞

    JavaScript

    的解析,此外因為生成

    Render Tree

    時需要

    CSSOM

    ,是以在

    DOM Tree

    解析完成而

    CSSOM

    未完成時不會繼續生成

    Render Tree

  • HTML

    結構同樣不會阻塞

    CSS

    解析的過程,也同樣不會和

    JavaScript

    的解析過程并行執行,并且

    DOM Tree

    解析未完成而

    CSSOM

    完成時同樣不會繼續生成

    Render Tree

  • 使用異步加載的

    <script>

    标簽是不會阻塞

    DOM

    解析的,當然其就不會阻塞

    DOMContentLoaded

    事件的觸發,但是依舊會阻塞

    load

    事件的觸發。

再來看一下

DOMContentLoaded

事件與

load

事件的觸發時機:

  • 當初始的

    HTML

    DOMContentLoaded

    事件被觸發,而無需等待樣式表、圖像和子架構的完全加載。關于觸發的時機,如果文檔中全部為

    HTML

    CSS

    DomContentLoaded

    事件無需等到

    CSS

    加載完畢即可觸發;當

    Js

    都在

    CSS

    之前

    DomContentLoaded

    CSS

    加載完畢即可觸發,當然解析

    CSS

    DOM

    是需要等待前邊的

    Js

    解析完畢的;當

    Js

    CSS

    之後時,則

    DomContentLoaded

    事件需等到

    CSS

    Js

    加載完畢才能夠觸發,上文也提到了

    CSS

    的加載會阻塞

    Js

    的加載,而

    Js

    标簽本身也屬于

    DOM

    結構,必須等待其加載完成之後才能觸發

    DomContentLoaded

    事件;異步加載的

    <script>

    标簽不會阻塞

    DOMContentLoaded

    事件。
  • 當整個頁面及所有依賴資源如樣式表和圖檔都已完成加載時,将觸發

    load

    事件。不使用動态加載的

    <iframe>

    同樣會阻塞

    load

    事件,此外即使是異步加載的

    <script>

    标簽同樣會阻塞

    load

在各種條件下重新整理一下頁面加載的過程,主要是在于

DOMContentLoaded

load

事件觸發的時間線:

  • HTML

    DOM Tree

    ,此時

    document.readyState = "loading"

  • <link>

    <style>

    CSS

    CSSOM

    HTML

    CSS

  • 解析到沒有設定異步加載的

    <script>

    的時候,阻塞文檔解析,等待

    Js

    腳本加載并且執行完成後,才會繼續解析文檔。
  • 解析到異步

    <script>

    的時候不阻塞解析文檔,繼續向下解析,

    defer

    屬性會使

    Js

    檔案等待

    DOM Tree

    建構完成之後再執行,而

    async

    Js

    檔案在下載下傳完成後立即執行。
  • 解析文檔的時候遇到需要加載外部資源例如圖檔時,先解析這個節點,根據

    src

    建立加載線程,異步加載圖檔資源,不阻塞解析文檔,當然浏覽器對于一個域名能夠開啟最大的線程數量會有限制。
  • 文檔解析完成,

    document.readyState = "interactive"

  • 設定為

    defer

    屬性的

    <script>

    腳本開始按照順序執行。
  • 觸發

    DOMContentLoaded

  • 等待設定為

    async

    <script>

    以及圖檔與

    <iframe>

    等加載,直至頁面完全加載完成。
  • load

    事件觸發,

    document.readyState = "complete"

調用

有些時候我們希望盡快介入對

DOM

的幹涉,此時調用

DOMContentLoaded

事件顯然更加合适,而為了處理各種浏覽器,需要對其進行相容處理。

  • 對于支援

    DOMContentLoaded

    的浏覽器使用

    DOMContentLoaded

  • 如果是小于

    525

    版本的

    Webkit

    則通過輪詢

    document.readyState

    來實作。
  • 對于舊版本的

    IE

    浏覽器使用

    Diego Perini

    發現的著名

    hack

/* https://www.cnblogs.com/JulyZhang/archive/2011/02/12/1952484.html */
/*
 * 注冊浏覽器的DOMContentLoaded事件
 * @param { Function } onready [必填]在DOMContentLoaded事件觸發時需要執行的函數
 * @param { Object } config [可選]配置項
 */
function onDOMContentLoaded(onready, config) {
    //浏覽器檢測相關對象,在此為節省代碼未實作,實際使用時需要實作。
    //var Browser = {};
    //設定是否在FF下使用DOMContentLoaded(在FF2下的特定場景有Bug)
    this.conf = {
        enableMozDOMReady: true
    };
    if (config)
        for (var p in config)
            this.conf[p] = config[p];
    var isReady = false;

    function doReady() {
        if (isReady) return;
        //確定onready隻執行一次
        isReady = true;
        onready();
    }
    /*IE*/
    if (Browser.ie) {
        (function() {
            if (isReady) return;
            try {
                document.documentElement.doScroll("left");
            } catch (error) {
                setTimeout(arguments.callee, 0);
                return;
            }
            doReady();
        })();
        window.attachEvent('onload', doReady);
    }
    /*Webkit*/
    else if (Browser.webkit && Browser.version < 525) {
        (function() {
            if (isReady) return;
            if (/loaded|complete/.test(document.readyState))
                doReady();
            else
                setTimeout(arguments.callee, 0);
        })();
        window.addEventListener('load', doReady, false);
    }
    /*FF Opera 高版webkit 其他*/
    else {
        if (!Browser.ff || Browser.version != 2 || this.conf.enableMozDOMReady)
            document.addEventListener("DOMContentLoaded", function() {
                document.removeEventListener("DOMContentLoaded", arguments.callee, false);
                doReady();
            }, false);
        window.addEventListener('load', doReady, false);
    }
}
           

每日一題

https://github.com/WindrunnerMax/EveryDay
           

參考

https://juejin.im/post/6844903667733118983
https://juejin.im/post/6844903535314731021
https://juejin.im/post/6844903623583891469
https://juejin.im/post/6844904072340832264
https://juejin.im/post/6844904176569286669
https://www.cnblogs.com/caizhenbo/p/6679478.html
https://www.cnblogs.com/rubylouvre/p/4536334.html
https://developer.mozilla.org/zh-CN/docs/Web/Events/DOMContentLoaded
https://gwjacqueline.github.io/%E5%BC%82%E6%AD%A5%E5%8A%A0%E8%BD%BDjs/