DOM樹的解析
2019-1-25 更新:
看到一篇寫的不錯的英文,語法很簡單,都可以看懂,講述了瀏覽器怎么加載網(wǎng)頁朴恳,有機會的話,先看完在做翻譯允蚣。
https://andydavies.me/blog/2013/10/22/how-the-browser-pre-loader-makes-pages-load-faster/
里面介紹了:How the pre-loader improves network utilisation
When the browser is blocked on a script, a second lightweight parser scans the rest of the markup looking for other resources e.g. stylesheets, scripts, images etc., that also need to be retrieved.
// 當(dāng)瀏覽器解析被script標簽阻塞的時候于颖,另一個輕量級的解析起掃描余下的文檔,尋找那么需要被取回的資源:css js img等等
當(dāng)拿到html的內(nèi)容之后嚷兔,有一套特定的語法規(guī)則來解析html森渐,解析文檔是指將文檔轉(zhuǎn)化成為可讓代碼理解和使用的結(jié)構(gòu)做入,包括詞法分析和語法分析
瀏覽器的解析器基本可以分為兩個階段:標記化和樹構(gòu)建
標記化是詞法分析過程
將輸入內(nèi)容解析成多個標記。HTML 標記包括起始標記同衣、結(jié)束標記母蛛、屬性名稱和屬性值。
標記生成器識別標記乳怎,傳遞給樹構(gòu)造器彩郊,然后接受下一個字符以識別下一個標記;如此反復(fù)直到輸入的結(jié)束
標記化算法的輸入結(jié)果是HTML標記蚪缀,使用狀態(tài)機表示秫逝。狀態(tài)機一共有4個狀態(tài):
- 數(shù)據(jù)狀態(tài)(Data)、
- 標記打開狀態(tài)(Tag open)询枚、
- 標記名稱狀態(tài)(Tag name)违帆、
- 關(guān)閉標記打開狀態(tài)(Close tag open state)。
標記化算法過程:
<html><body>Hello world</body></html>
- 初始狀態(tài)是數(shù)據(jù)狀態(tài)
- 遇到字符 < 時金蜀,狀態(tài)更改為“標記打開狀態(tài)”
- 接收一個 a-z 字符會創(chuàng)建“起始標記”刷后,狀態(tài)更改為“標記名稱狀態(tài)”
- 這個狀態(tài)會一直保持到接收 > 字符。在此期間接收的每個字符都會附加到新的標記名稱上渊抄,
- 遇到 > 標記時尝胆,會發(fā)送當(dāng)前的標記,狀態(tài)改回“數(shù)據(jù)狀態(tài)”护桦,本例子的標記名稱為 html
- <body> 標記也會進行同樣的處理
- 現(xiàn)在我們回到“數(shù)據(jù)狀態(tài)”含衔。接收到 Hello world 中的 H 字符時,將創(chuàng)建并發(fā)送字符標記
- 現(xiàn)在我們回到“標記打開狀態(tài)”二庵。接收下一個輸入字符 / 時贪染,會創(chuàng)建 end tag token 并改為“標記名稱狀態(tài)”
- 我們會再次保持這個狀態(tài),直到接收 >催享。然后將發(fā)送新的標記杭隙,并回到“數(shù)據(jù)狀態(tài)”
樹構(gòu)建整理來自這個博客 https://blog.csdn.net/Alan_1550587588/article/details/80297765
樹構(gòu)建又叫做語法分析
樹構(gòu)建算法:
- 在創(chuàng)建解析器的同時,也會創(chuàng)建 Document 對象
- 樹構(gòu)建器接收到標簽解析器發(fā)來的起始標簽名后因妙,會加入到棧中痰憎,此時還未向DOM樹中添加任何結(jié)點
- 當(dāng)接收到一個帶有</>結(jié)束標簽,此時查詢棧頂元素兰迫,
如果和傳入的結(jié)束標簽屬于同種類型的標簽信殊,則將棧頂元素彈出,向DOM樹中加入此節(jié)
點汁果,然后繼續(xù)向下解析
- 如果遇到的是沒有封閉標簽的元素如<img/>涡拘,則直接加入DOM樹中即可,無需入棧据德。
- 依次向下解析鳄乏,當(dāng)棧為空跷车,即<html>根節(jié)點也加入到DOM樹中,DOM樹構(gòu)建完畢橱野。
注意??:
這里需要指出的是朽缴,當(dāng)某個元素缺失結(jié)束標簽時,棧頂元素是<div>水援,
但是此時傳來的結(jié)束標簽是</body>密强,兩者不是同一種標簽,說明div缺少了結(jié)束標簽蜗元,
這種情況也將棧頂<div>元素彈出或渤,加入到DOM樹中。
相當(dāng)于給<div>補了一個</div>結(jié)束標簽奕扣,這就是解析器的高容錯機制薪鹦。
所以解析的html的過程中
- 遇到link標簽,會重新開一個下載線程去下載惯豆,html解析不會中斷池磁,
- 遇到script標簽的時候會等待文件下載完成,立即執(zhí)行楷兽,執(zhí)行完畢后再向下解析地熄,
會阻塞html的dom解析,才會有優(yōu)化html結(jié)構(gòu)的時候拄养,把css放在頭部离斩,把script放在尾部 - 遇到img標簽的時候银舱,也和css一樣瀏覽器重新開一個下載線程去下載瘪匿,不會阻塞dom的解析
defer和async
https://segmentfault.com/a/1190000008299659
defer 是一個延遲執(zhí)行的標示 當(dāng)<script defer>的時候告訴瀏覽器,異步下載 不影響dom的parse,我猜背后的原理應(yīng)該和link標簽一樣寻馏,也是瀏覽器另開了一個線程去下載棋弥,下載完了之后不去立刻執(zhí)行,控制權(quán)沒有給到j(luò)s引擎線程诚欠,所以渲染線程才不受影響顽染,能繼續(xù)解析dom樹,dom樹加載完成在onload事件觸發(fā)之前執(zhí)行,
async是可以異步下載轰绵,但是下載完了之后立即執(zhí)行粉寞,下載還是另開一個線程需下載的,所以不影響dom parse 但是下載完了立刻去解析左腔,所以唧垦,控制權(quán)交給js引擎線程,所以解析被block了