「從瀏覽器輸入url到頁面顯示經歷了哪些?」
一個很是
常見的問題,看了該系列絕對能驚到面試官,可能就由於這一道面試題就收了你呢!嘿嘿。做者簡介:koala,專一完整的 Node.js 技術棧分享,從 JavaScript 到 Node.js,再到後端數據庫,祝您成爲優秀的高級 Node.js 工程師。【程序員成長指北】做者,Github 博客開源項目 github.com/koala-codin…javascript
DOM是Document Object Model(文檔對象模型)的縮寫css
W3C 文檔對象模型 (DOM) 是中立於平臺和語言的接口,它容許程序和腳本動態地訪問和更新文檔的內容、結構和樣式。-這是W3Cschool給的概念html
看了上面的概念好像太「官方」,解釋就是 DOM 是對 HTML 文檔結構化的表述,後端服務器返回給瀏覽器渲染引擎的 HTML 文件字節流是沒法直接被瀏覽器渲染引擎理解的,要轉化爲渲染器引擎能夠理解的內部結構,這個結構就是 DOM。 W3C 那個概念我好像尚未把它所有翻譯完,「容許程序和腳本動態地訪問和更新文檔的內容、結構和樣式」。這裏其實就是DOM的做用了前端
本文我主要以 Webkit 渲染引擎來說解,Safari 和 Chrome 都使用 Webkit。Webkit 是一款開源渲染引擎,它原本是爲 linux 平臺研發的,後來由 Apple 移植到 Mac 及 Windows 上。java
先看一張總體的流程圖linux
下面圍繞這張圖和不一樣表明性對例子進行講解。從後端返回給瀏覽器渲染引擎 HTML 文件字節流, 第一步要通過的就是渲染引擎中的 HTML 解析器。它實現了將 HTML 字節流轉換爲 DOM樹 結構。 HTML 文件字節流返回的過程當中 HTML 解析器就一直在解析,邊加載邊解析哦(這裏注意下,有些文章寫的有問題)。git
例子1:最簡單的不帶 CSS 和 JavaScript 的 HTML 代碼講解 HTML 解析器程序員
<html>
<body>
<div>程序員成長指北</div>
</body>
</html>
複製代碼
根據這段代碼具體分析 HTML 解析器作了哪些事github
讀取 HTML 的原始字節流,並根據文件的指定編碼(例如 UTF-8)將它們轉換成各個字符。 並將字符串轉換成 W3C HTML5 標準規定的各類令牌,例如,「」、「」,以及其餘尖括號內的字符串。每一個令牌都具備特殊含義和一組規則。面試
一堆字節流 bytes
3C 62 6F ...
複製代碼
轉成正常的html文件
<html>
<body>
<div>
koala
<p>
程序員成長指北
</P>
</div>
</body>
</html>
複製代碼
分詞器將字節流轉換爲一個一個的 Token,Token 分爲 Tag Token和文本 Token,上面這段代碼最後分詞器轉化後的結果是:
HTML 解析器維護了一個 Token 棧結構(數據結構真是個好東西),這個棧結構的目的就是用來計算節點間的父子關係,在上一個階段生成的 Token 會被順序壓到這個棧中,如下是具體規則:
父節點
就是棧中相鄰的那個元素生成的 DOM節點父節點
就是當前棧頂 Token 所對應的 DOM 節點。此時應該搞懂了核心圖中 HTML 解析器的部分,和 DOM 樹的基本繪製流程,可是現實很殘酷,哪裏有這麼簡單的前端代碼,還有有 JavaScript 和 CSS 呢!繼續往下看
CSS 解析器最終的目的也是構建樹不過它構建的樹是 CSSOM 樹 樹的構建流程和 DOM 樹的構建流程基本相同
仍是那張圖,具體我就不一一講解一遍了。直接用這個簡單例子body { font-size: 16px }
div { font-weight: bold }
div p { display: none }
複製代碼
看下最後構造的 CSSOM 樹
CSSOM 爲什麼具備樹結構?爲頁面上的任何對象計算最後一組樣式時,瀏覽器都會先從適用於該節點的最通用規則開始(例如,若是該節點是 body 元素的子項,則應用全部 body 樣式),而後經過應用更具體的規則(即規則「向下級聯」)以遞歸方式優化計算的樣式。
以上面的 CSSOM 樹爲例進行更具體的闡述。span 標記內包含的任何置於 body 元素內的文本都將具備 16 像素字號,而且顏色爲紅色 — font-size 指令從 body 向下級聯至 span。不過,若是某個 span 標記是某個段落 (p) 標記的子項,則其內容將不會顯示。
注意點:
- CSS解析能夠與DOM解析同進行
- 若是隻有 CSS 和 HTML 的頁面,CSS 不會影響 DOM 樹的建立,可是若是頁面中還有 JavaScript,結論就不同了,請繼續往下看。
上面兩個例子中都尚未javascript的出現,接下來講下JavaScript 對 DOM 樹和 CSSOM 樹構建的影響。
狀況1:當前頁面中只有 Html 和 JavaScript,並且 JavaScript 非外部引入
DOM 樹構建時當遇到JavaScript腳本,就要暫停 DOM 解析,先去執行Javascript,由於在JavaScript可能會操做當前已經生成的DOM節點。
有一點須要注意:javascript是可能操做當前已經生成的DOM節點,若是是後面還未生成的DOM節點是不生效的,好比這段代碼:
<html>
<body>
<div>1</div>
<script> let div1 = document.getElementsByTagName('div')[0] div1.innerText = '程序員成長指北' let div2 = document.getElementsByTagName('div')[1] div2.innerText = 'kaola' </script>
<div>test</div>
</body>
</html>
複製代碼
顯示結果爲兩行: 第一行結果是程序員成長指北 第二行記過是test 由於在執行第三行和第四行 script 腳本的時候,DOM樹中尚未生成第二個 div對應的dom節點。
狀況2:當頁面中同時有Html JavaScript CSS ,並且都非外部引入
DOM 樹構建時當遇到 JavaScript 腳本,就要暫停 DOM 解析,先去執行 JavaScript,同時 JavaScript 還要判斷 CSSOM 是否解析完成,由於在 JavaScript 可能會操做 CSSOM 節點,CSSOM 節點確認解析完成,執行 JavaScript 再次回到 DOM 樹建立。(因此這裏也能夠所CSS解析間接影響DOM樹建立)
狀況3:當頁面中同時有Html,JavaScript, CSS ,並且外部引入
Webkit渲染引擎有一個優化,當渲染進程接收HTML文件字節流時,會先開啓一個預解析線程,若是遇到 JavaScript 文件或者 CSS 文件,那麼預解析線程會提早下載這些數據。當渲染進程接收 HTML 文件字節流時,會先開啓一個預解析線程,若是遇到 JavaScript 文件或者 CSS 文件,那麼預解析線程會提早下載這些數據。DOM樹在建立過程當中若是遇到JavaScript文件,接下來就和狀況2類型同樣了。
影響關係圖: 畫了一張影響關係圖但願你們更好的記憶:
經過 DOM 樹和 CSSOM 樹,瀏覽器就能夠經過兩者構建渲染樹了。瀏覽器會先從 DOM 樹的根節點開始遍歷每一個可見節點,而後對每一個可見節點找到適配的CSS樣式規則並應用。具體的規則有如下幾點須要注意:
Render Tree和DOM Tree不徹底對應。
請注意 visibility: hidden 與 display: none 是不同的。前者隱藏元素,但元素仍佔據着佈局空間(即將其渲染成一個空框),然後者 (display: none) 將元素從渲染樹中徹底移除,元素既不可見,也不是佈局的組成部分
看一下前問中提到的 DOM 樹和 CSSOM 樹最終合成的渲染樹結果是:
看完了渲染樹的造成,在開發過程當中咱們能作哪些優化?(注意這裏的優化只是針對渲染樹造成部分,其餘的優化會在系列文章以後繼續講)
看完這篇文章趕忙檢測一下你寫的前端代碼,腦補一下渲染樹造成過程,想一想本身代碼有沒有須要改善的地方,系列文章會繼續分享,下篇該系列文章渲染樹的佈局與繪製以及虛擬DOM樹出現的必要性,感謝觀看。
極客時間瀏覽器專欄
瀏覽器渲染原理: srtian96.gitee.io/blog/2018/0…
以爲不錯點個Star,歡迎 加羣 互相學習。