瀏覽器渲染原理

瀏覽器渲染整體來講分爲如下幾步javascript

  1. 瀏覽器經過HTTP或者HTTPS協議,先服務端請求頁面
  2. 把請求回來的HTML解析成DOM樹
  3. 把CSS解析成CSSOM樹
  4. 把DOM樹和CSSOM樹組合在一塊兒,生成渲染樹(RENDER TREE)
  5. 經過渲染樹計算出佈局(layout)
  6. 渲染引擎會遍歷Render樹,繪製(painting)到界面上

網絡部分放到TCP協議中,在這就很少說了css

渲染流程

構建DOM樹、CSSOM樹

DOM樹和CSSOM樹的構建流程很是像,就以DOM樹爲例html

瀏覽器從服務器獲取的是16進制文件流,好比3C 62 6F 64 79 3E 48 65.....,瀏覽器要把16進制的Bytes轉化成字符串,再遍歷這個字符串解析成tokensvue

瀏覽器是怎麼將字符串解析成tokens的。使用的方法是狀態機java

狀態機怎麼執行的react

  • 接受到<字符。多是一個標籤的開頭,開啓一個狀態
    • 下一個字符是字母,就是標籤名
    • 下一個字符是!,就是註釋
    • ....
  • 接受到非<字符。多是一個文本節點,開啓一個狀態
    • ....

瀏覽器一步步將文件流轉化爲字符串再經過狀態機轉化爲token,獲得token後,按照W3C規則轉換成DOM樹。css3

簡單總結下:後端

  1. 瀏覽器邊接受文件流(進制編碼內容)邊編譯爲token
  2. 按照w3c規則進行字符解析,生成對應的Tokens,最後轉換爲瀏覽器內核能夠識別渲染的DOM節點
  3. 按照節點最後解析爲對應的 DOM TREE、CSSOM TREE

須要注意的事:瀏覽器

  • DOM樹構建過程可能會由於css、js而阻塞
  • DOM樹構建與CSSOM樹構建能夠同時進行
  • 不可見標籤也會出如今dom樹中
  • CSSOM樹構建過程可能會由於js而阻塞

構建渲染樹(Render Tree)

瀏覽器根據DOM樹和CSSOM樹生成帶有標籤和樣式信息的渲染樹(Render Tree)。渲染樹與DOM樹不是一一對應的關係,不顯示的節點不會出如今渲染樹上。緩存

佈局(Layout)

根據渲染樹提供的節點和樣式,計算元素在視口中的確切的大小和位置。

繪製(paining)

將元素計算後的大小和位置渲染到頁面上的過程。渲染樹的繪製工做是瀏覽器調用UI後端組件完成的。

迴流和重繪(reflow和repaint)

一些操做會引起元素位置或者大小的變化,這樣瀏覽器須要從新進行Lauout計算(迴流/重排),重排完成後,瀏覽器須要從新繪製(重繪)。

  • 第二次或屢次佈局(Lauout)就是迴流/重排(reflow)
  • 第二次或屢次繪製(paining)就是重繪(repaint)

若是是改變一些基礎樣式好比顏色,則不須要重排,只須要重繪便可。

  • 重繪:元素樣式改變
    • 例如:color、visibility...
  • 迴流:元素大小、位置發生變化
    • 例如:添加刪除元素、視口大小改變...

重繪不必定會迴流,可是迴流必定會觸發重繪

性能優化:減小DOM的迴流

  1. 放棄傳統操做dom的時代,基於vue/react開始數據影響視圖模式(mvvm/mvc/virtual dom/dom diff....)
  2. 分離讀寫操做(如今的瀏覽器都有渲染隊列的機制)
  3. 樣式集中改變
  4. 緩存佈局信息
  5. 元素批量修改
  6. 使用DocumentFragment將須要屢次修改的DOM元素緩存,最後一次性append到真實DOM中渲染
  7. 變化多的元素脫離文檔流,造成新的Render Layer,下降回流成本
  8. css3硬件加速(GPU加速)(會佔用大量內存)

資源加載

瀏覽器自上而下讀取代碼,讀取到資源文件

css

使用css有三種方式:使用link、@import、內聯樣式,其中link和@import都是導入外部樣式。它們之間的區別:

  • link:瀏覽器會派發一個新等線程(HTTP線程)去加載資源文件,與此同時GUI渲染線程會繼續向下渲染代碼
  • @import:GUI渲染線程會暫時中止渲染,去服務器加載資源文件,資源文件沒有返回以前不會繼續渲染(阻礙瀏覽器渲染)
  • style:GUI直接渲染

另外外部樣式若是長時間沒有加載完畢,瀏覽器爲了用戶體驗,會使用瀏覽器會默認樣式,確保首次渲染的速度。因此css通常寫在headr中,讓瀏覽器儘快發送請求去獲取css樣式。

javascript

JavaScript執行線程與GUI渲染線程不能同時執行,這就意味着執行js代碼勢必會阻塞頁面渲染。爲了避免阻塞頁面的渲染,能夠:

  • script標籤放在頁面的尾部,確保dom生成完再加載js
  • 儘量使用defer、async

關於<script><script defer><script async>的區別(配合圖片食用更佳)

  • <script>:當即中止頁面渲染去加載資源文件,當資源加載完畢後當即執行js代碼,js代碼執行完畢後繼續渲染頁面
  • <script defer>:開闢新的線程去加載資源文件,當資源加載完畢後等待頁面渲染,頁面渲染完畢後再執行js代碼
  • <script async>:開闢新的線程去加載資源文件,當資源加載完畢後當即執行js代碼,js代碼執行完畢後繼續渲染頁面(特別注意:多個async js執行順序是按照加載完畢的順序,非js請求順序)

\ 阻塞頁面渲染(GUI線程) 當即加載js資源 js加載完畢後當即執行 按照script標籤順序執行腳本
script
defer
async

補充:window.onload 和 DOMContentLoaded 的區別

onload:是頁面資源加載完畢,包括圖片、視頻資源

DOMContentLoaded:DOM渲染完成

性能優化

瞭解這麼多,最終仍是要爲了性能優化服務。除了已經提過的減小回流的優化外,還有:

  • 減小DOM樹渲染的時間
    • HTML層級不要太深
    • 標籤語義化(減小不標準語義化的特殊處理)
  • 減小CSS樹渲染時間
    • 減小層級嵌套(選擇器是從右向左解析的)(less、sass嵌套是個大坑,注意)
  • 減小資源加載時間
    • 利用瀏覽器並行加載資源次數、請求大小,不要太少也不要太多(6-7)
    • 通常會把css放在頁面開始位置,提早請求
      • 使用link,不用@import
      • 若是css少,儘量採用內嵌式
    • ssr 減小數據首頁數據的請求
    • 使用骨架屏、loding(感官上的提升,不會實際提升速度)
    • 避免阻塞的js加載
      • js放在頁面底部
      • 儘量使用defer、async
相關文章
相關標籤/搜索