通俗的说下浏览器的渲染过程
最初的模型:
瀏覽器下載 html
-
開始解析 html
遇見外鏈資源, 保存起來, 并且繼續解析
html 解析結束
-
開始下載外鏈
-
下載結束
-
開始處理
css 處理
-
js 處理
處理完畢, 開始渲染
用戶看到界面
-
-
這個模型的基礎是: 瀏覽器是單線程的.
但是實際上: 瀏覽器不是單線程, 是多個線程.
瀏覽器有如下幾個線程:
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 下載結束開始執行, 啪的一下把頁面
這種情況肯定不能允許發生.
為了避免這種情況, 可以這樣
先全局檢測下是不是有 script 標簽, 如果有的話, html 就等著 script 加載執行之后,
再開始渲染.
這種方式有一個不好: 也就是說, 即使我們現在有了 html 和 css, 其實都可以把頁面渲染出來了
但是還是要等 script 下載執行之后才敢進行渲染, 有點投鼠忌器的感覺.
有時候等半天, 可能 script 返回的就一句話:
而且全局檢測 script 標簽, 這句話說的簡單, 實際上是要建立在你已經把 html 解析結束了之后才知道
到底有沒有 script 標簽.
所以實際的情況是:
瀏覽器不知道頁面里面有沒有 script 標簽 不知道script 里面會不會有 DOM 操作, 是 '中獎啦' 還是 '逗你玩'面對這種情況, 實際上只能賭, 或者說博弈.
瀏覽器拿到 html 和 css 之后依舊開始解析渲染
為了減小萬一中獎之后全盤都輸的情況, 當遇見 script 之后
所以更新策略:
瀏覽器遇見 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 依賴.
總結
以上是生活随笔為你收集整理的通俗的说下浏览器的渲染过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在Kubernetes中暴露服务访问
- 下一篇: 虚拟内存(VirtualAlloc),堆