通俗的說下瀏覽器的渲染過程

最初的模型:

  • 瀏覽器下載 htmljavascript

  • 開始解析 htmlcss

    • 碰見外鏈資源, 保存起來, 而且繼續解析html

    • html 解析結束java

    • 開始下載外鏈瀏覽器

      • 下載結束dom

        • 開始處理async

          • css 處理google

          • js 處理線程

            • 處理完畢, 開始渲染code

            • 用戶看到界面

這個模型的基礎是: 瀏覽器是單線程的.

可是實際上: 瀏覽器不是單線程, 是多個線程.
瀏覽器有以下幾個線程:
1 javascript引擎線程
2 界面渲染線程
3 瀏覽器事件觸發線程
4 http請求線程

也就是說: 下載和解析是能夠同步的, 碰見外鏈就開始下載.

更改以後的模型

  • 瀏覽器下載 html

  • 開始解析 html

    • 碰見外鏈資源, 開始下載, 而且繼續解析

    • html 解析結束

    • 下載結束

      • 開始處理

        • css 處理

        • js 處理

          • 處理完畢, 開始渲染

          • 用戶看到界面

這個模型的基礎是:
資源下載和 html 解析是同步的, 全部的資源下載結束, 纔開始進行下一步:渲染.
實際情景是:
資源大體能夠分紅
css
js
imgs
others

imgs以及 others 這種, 若是一個資源過大, 好比說一個媒體文件100M, 非要等到用戶下載結束
纔開始下一步, 這顯然是不合理的.
而 css 和 js 是能夠對頁面產生修改和效果的, 因此必需要等待它們的參與才能進行下一步操做,
好比說 css,js 都沒有下載解析執行結束, 就開始下一步渲染, 最終渲染的結果是一個沒有樣式的
頁面.

看瀏覽器是否是這麼想的?
實驗:
在demo 中加一個 p 標籤, 在底部加一個 css 外鏈
若是 css 都沒有加載完畢, p 標籤就顯示出來了, 說明瀏覽器就沒有等 css 文件
也就是說: html 解析結束以後, 什麼都無論, 就開始下一步渲染了.

demo:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
</head>
<body>
    <p>hello world</p>
    <link rel="stylesheet" href="https://www.google.com.hk/test.css">
</body>
</html>

結果:
css 沒有加載以前, 頁面空白, 說明 html 解析結束以後, 會等到css加載出來再開始渲染.

更一步的實驗:
資源變成 js, 頁面先渲染出來, js 還在加載.
資源變成 img, 頁面會先渲染出來, img 還在加載.

結論:
**html 解析結束以後, 會先等到 css 下載和解析結束以後(經過 link 標籤知道是 css 文件)
再開始下一步, 並不會等全部的非 js 資源.**

因此模型變成:

  • 瀏覽器下載 html

  • 開始解析 html

    • 碰見外鏈資源, 開始下載, 而且繼續解析

    • html 解析結束

    • 等到css下載結束

      • 開始處理 css

        • 處理完畢, 開始渲染

        • 用戶看到界面

如今考慮一種狀況:
js 是有能力去改變 DOM, 那麼若是都渲染結束了, js 這個時候開始執行了,而後把頁面從新干掉了.
這個時候怎麼辦?

只能將修改應用到已經渲染好的頁面上.

考慮一種極端狀況, 頁面裏面有成千上萬的節點, 好比說1萬個節點, css 文件都 一兩百k
辛辛苦苦瀏覽器把頁面渲染出來了, 而後這個時候, js 下載結束開始執行, 啪的一下把頁面

document.write('中獎啦');

這種狀況確定不能容許發生.

爲了不這種狀況, 能夠這樣
先全局檢測下是否是有 script 標籤, 若是有的話, html 就等着 script 加載執行以後,
再開始渲染.

這種方式有一個很差: 也就是說, 即便咱們如今有了 html 和 css, 其實均可以把頁面渲染出來了
可是仍是要等 script 下載執行以後纔敢進行渲染, 有點投鼠忌器的感受.
有時候等半天, 可能 script 返回的就一句話:

console.log('逗你玩');

並且全局檢測 script 標籤, 這句話說的簡單, 其實是要創建在你已經把 html 解析結束了以後才知道
到底有沒有 script 標籤.

因此實際的狀況是:

瀏覽器不知道頁面裏面有沒有 script 標籤
不知道script 裏面會不會有 DOM 操做, 是 '中獎啦' 仍是 '逗你玩'

面對這種狀況, 實際上只能賭, 或者說博弈.

瀏覽器拿到 html 和 css 以後依舊開始解析渲染
爲了減少萬一中獎以後全盤都輸的狀況, 當碰見 script 以後

中止解析, 專心下載 js 文件
    這樣即便中獎, 我也就渲染了前面了一點內容, 後面的尚未渲染, 輸少點
    可是這樣後面若是還有資源要加載。。。

因此更新策略:

瀏覽器碰見 script, 開始下載, 把後面的html 解析掉, 全部的資源都開始下載
而後回頭安心等這個 script, 看看到底中獎不中獎.

因此模型變成:

  • 瀏覽器下載 html

  • 開始解析 html

    • 碰見外鏈, 開始下載

      • 發現 script 外鏈, 繼續html解析

        • 將頁面分紅兩部分, script 標籤以前, 以後

          • 處於 script 以前的頁面

            • css 下載結束

              • 渲染

              • 處於 script 以後的頁面

                • script 下載完畢, 執行

                  • css 下載完畢

                    • 渲染

瀏覽器不是等全部的資源都下載結束纔開始渲染
瀏覽器也不是等到全部的 js 都執行結束以後纔會渲染

具體的渲染過程

當 html 解析成 DOM tree, css 解析成 CSSOM, 兩者合併成
Render Tree, 就能夠開始渲染了.

首先要先計算這棵樹上面的全部的節點的位置, 這一步叫作 layout
而後要給每一個節點上色, 這一步叫作 paint
layout 和 paint 統稱爲 render.

當頁面的元素的位置修改以後, 就會出現 relayout (重繪)
relayout 一定會形成 repaint.

附錄: defer, async 之間的區別.

defer
以前說了, 瀏覽器碰見 js 以後會始終等着它執行, 後面的內容都不渲染了
而後常常就報錯了, 我草獲取某個節點怎麼沒有, 想要獲取後面的節點可是節點尚未
渲染出來, 因此呢, 因此就有了 defer
至關於說, 我等你所有渲染以後再執行吧, 要否則我總是出錯, 煩人.
執行要在全部元素解析完成以後,DOMContentLoaded 事件觸發以前完成。

async還有一種狀況, 就是說, 瀏覽器辛辛苦苦等到 js 下載執行結束髮現哎吆我草這個玩意對dom屁改動都沒有, 我等她幹嗎啊, 我草草, 那就出來 async這個東西就是說, 不須要等我, 也不須要關心個人執行, 我不會干擾你的, 你作本身的事情就好.同時也解決了多個js之間的依賴, 加上這個就表示, 我是孤立的, 我不依賴任何其餘js也不給任何其餘js 依賴.

相關文章
相關標籤/搜索