天天看點

浏覽器渲染頁面常見問題

建構過程中可能會産生的阻塞

  • ​html​

    ​​的代碼,是從上到下一行行執行的,也就是說如果​

    ​js​

    ​​代碼寫在​

    ​head​

    ​​頭裡,且沒有用加在​

    ​window.onload​

    ​​方法裡,那麼他是無法讀取到​

    ​body​

    ​​裡的标簽的。之是以加在​

    ​window.onload​

    ​​裡可以執行,是因為,​

    ​window.onload​

    ​​裡的函數會在​

    ​dom​

    ​樹加載之後執行。
  • 在遇到​

    ​link​

    ​​标簽後,會在​

    ​link​

    ​​加載(從伺服器下載下傳)完畢後,再執行後續代碼。但與此同時,如果還有外部檔案,則是同時加載(不阻塞後續外部檔案​

    ​link​

    ​​、​

    ​script​

    ​加載)。但是外部檔案内的代碼不會執行,隻會在代碼解析到它的時候執行。
  • ​script​

    ​​标簽會阻塞​

    ​html​

    ​​解析,因為​

    ​js​

    ​​可能會改變​

    ​dom​

    ​​和​

    ​css​

    ​​,是以浏覽器會先解析script,避免浪費時間。 要想避免阻塞的話,可使用​

    ​defer​

    ​​和​

    ​async​

    ​。
  • 對于動态建立的​

    ​link​

    ​​标簽不會阻塞其後動态建立的​

    ​script​

    ​​的加載與執行,不管​

    ​script​

    ​​标簽是否具有​

    ​async​

    ​屬性。
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <script>
        var start = +new Date
    </script>

</head>

<body>
    test
    <script>
        var link = document.createElement('link')
        link.href = 'http://udacity-crp.herokuapp.com/style.css?rtt=2'
        link.rel = 'stylesheet'
        document.head.appendChild(link)
    
        var script = document.createElement('script')
        script.src = 'http://udacity-crp.herokuapp.com/time.js?rtt=1&a'
        document.head.appendChild(script)
    </script>
    <div id="result"></div>
    <script>
        var end = +new Date
        document.getElementById('result').innerHTML = end - start
    </script>
</body>
</html>      
  • ​js​

    ​​添加​

    ​async​

    ​​屬性之後,​

    ​script​

    ​​加載的外部檔案成為了異步加載,這時相當于它于原本的​

    ​html​

    ​​解析過程同步進行。是以他不會被任何加載過程阻塞,隻會在自己加載完成之後執行。但是,異步執行的影響就是,它如要讀取​

    ​dom​

    ​​節點,很可能會失敗,因為它的加載和​

    ​html​

    ​​解析過程沒有了先後順序。另外,如果它要輸出動态的​

    ​dom​

    ​​節點,就無法保證節點的位置,因為它添加的節點,是在​

    ​html​

    ​已解析的節點下順序添加的。
<script src="http://localhost:8080/test.js" async></script>      
  • ​js​

    ​​添加​

    ​defer​

    ​​屬性之後,​

    ​script​

    ​​加載的外部檔案成為了異步加載,執行是同步的。腳本加載不阻塞頁面的解析,腳本在擷取完後并不立即執行,而是等到​

    ​DOM​

    ​樹加載完畢執行。
  • 浏覽器渲染頁面常見問題
  • 如下代碼,加上​

    ​defer​

    ​​後會報錯​

    ​Uncaught ReferenceError: $ is not defined​

    ​​。​

    ​DOM​

    ​​樹渲染結束前​

    ​body​

    ​​裡的​

    ​script​

    ​已經執行了
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JS Bin</title>
    <script defer src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>

<body>
    <div id="d1">我是内容</div>
    <script >
        console.log($('#d1').html())
    </script>
</body>
</html>      
  • async 屬性表示異步執行引入的 JavaScript,與 defer 的差別在于,如果已經加載好,就會開始執行——無論此刻是 HTML 解析階段還是 DOMContentLoaded 觸發之後。
  • defer 屬性表示延遲執行引入的 JavaScript,即這段 JavaScript 加載時 HTML 并未停止解析,這兩個過程是并行的。整個 document 解析完畢且 defer-script 也加載完成之後(這兩件事情的順序無關),會執行所有由 defer-script 加載的 JavaScript 代碼,然後觸發 DOMContentLoaded 事件
  • defer 與相比普通 script,有兩點差別:載入 JavaScript 檔案時不阻塞 HTML 的解析,執行階段被放到 HTML 标簽解析完成之後。

什麼情況會引起重排/回流(reflow)

  • 添加或者删除可見的DOM元素;
  • 元素位置改變——display、float、position、overflow等等;
  • 元素尺寸改變——邊距、填充、邊框、寬度和高度
  • 内容改變——比如文本改變或者圖檔大小改變而引起的計算值寬度和高度改變;
  • 頁面渲染初始化;
  • 浏覽器視窗尺寸改變——resize事件發生時;

如何減少和避免重排

  • Reflow 的成本比 Repaint 的成本高得多的多。一個節點的 Reflow 很有可能導緻子節點,甚至父節點以及兄弟節點的 Reflow 。
  • 樣式集中改變
  • 盡量不要在布局資訊改變時做查詢(會導緻渲染隊列強制重新整理)
  • 同一個DOM的多個屬性改變可以寫在一起(減少DOM通路,同時把強制渲染隊列重新整理的風險降為0)
  • 如果要批量添加DOM,可以先讓元素脫離文檔流,操作完後再帶入文檔流,這樣隻會觸發一次重排(fragment元素的應用)
  • 将需要多次重排的元素,position屬性設為absolute或fixed,這樣此元素就脫離了文檔流,它的變化不會影響到其他元素。例如有動畫效果的元素就最好設定為絕對定位。

DOM是什麼?

DOM(Document Object Model——文檔對象模型)是用來呈現以及與任意 HTML 或 XML文檔互動的API。DOM 是載入到浏覽器中的文檔模型,以節點樹的形式來表現文檔,每個節點代表文檔的構成部分(例如:頁面元素、字元串或注釋等等)。

浏覽器渲染頁面常見問題

DOM的作用

  • DOM 将HTML文檔呈現為帶有元素、屬性和文本的樹結構(節點樹)。
  • 它允許運作在浏覽器中的代碼通路檔案中的節點并與之互動。節點可以被建立,移動或修改。事件監聽器可以被添加到節點上并在給定事件發生時觸發。

什麼是DOM渲染?

DOM樹的建構是文檔加載完成開始的?

Render樹是DOM樹和CSSOM樹建構完畢才開始建構的嗎?

繼續閱讀