瀏覽器工作原理(22) - JavaScript是如何影響DOM樹構建的?
上一篇文章我們講了chrome效能皮膚的使用,瞭解了請求過程中的幾個效能指標,這篇文章我們一起來看一下DOM樹是如何生成的,本文主要有兩大塊內容:第一個是解析過程中遇到JavaScript指令碼,DOM解析器是如何處理的?第二個是DOM解析器是如何處理跨站點資源的?
什麼是DOM
從網路傳給渲染引擎的是HTML文件位元組流,並無法執行,需要轉換為渲染引擎能夠識別的DOM樹,在渲染引擎中,DOM有三個層面的作用:
- 從頁面的視覺來看,DOM是生成頁面的基礎資料結構
- 從JavaScript指令碼的角度來看,DOM提供給JavaScript指令碼一些API介面,方便JavaScript操作頁面元素
- 從解析安全的角度來看,DOM是一道攔截器,不符合標準的DOM會提前報錯
總結來說,DOM就是頁面的結構化描述,並可以提供給指令碼一些介面,方便來操作頁面元素,還具備一定的過濾功能
DOM樹如何生成
在渲染引擎內部,負責將HTML文件轉換成DOM結構的模組叫 HTML解析器(HTMLParser) ,那麼HTML解析器是如何工作的呢?
HTML解析器是在文件邊載入的過程中就開始執行解析的,也就是說載入了多少就解析多少
一起來看一下詳細的流程是怎麼樣的,首先是網路程式接收到響應頭,會根據響應頭中的content-type來判斷檔案的型別,比如“text/html”,那麼瀏覽器會判斷這是一個HTML型別的檔案,然後為這個請求建立一個渲染程式,建立好渲染程式,渲染程式和網路程式進行通訊,網路程式把接受到的資料不斷的通過共享資料通道傳送給渲染程式,渲染程式再將資料傳給HTML解析器,解析成DOM。
接下來再一起看看DOM的具體生成流程—位元組流轉換成DOM
從上圖可以看出,位元組流轉換成DOM需要三個階段:
第一個階段,通過分詞器將位元組流轉換為Token
V8執行JavaScript指令碼的時候,會將程式碼先做詞法分析,將JavaScript分解為一個個的Token,這裡HTML的解析也是一樣的,先進行詞法分析,將位元組流轉換為Token,Tag Token 和 文字Token
第二階段和第三階段同步執行,生成DOM節點,將DOM 節點新增到DOM樹中
HTML解析器維護了一個Token棧結構,主要用來計算節點之間的父子關係,第一階段生成的Token按照順序壓入棧中,startTag Token首先壓入棧中,並生成DOM樹,文字節點直接新增到DOM樹不入棧,遇到Endtag Token,則會去棧頂開始找對應的Starttag Token,並彈出Starttag Token,表示該元素已解析完成,直到所有的Token解析完成,示意圖如下:
JavaScript如何影響DOM生成
看一段下面的程式碼:
<html>
<body>
<div>1</div>
<script>
let div1 = document.getElementsByTagName('div')[0]
div1.innerText = 'example'
</script>
<div>test</div>
</body>
</html>
解析這段HTML文件時,遇到script標籤之前都是一樣的過程,遇到script,解析器會判斷這是一段指令碼,HTML解析器會停止當前工作,開始執行指令碼,這時候JavaScript引擎開始工作,執行完指令碼,修改了div1的內容,然後又繼續執行DOM的解析過程
上面的指令碼是新增到了HTML內部,如果指令碼是引入過來的,又該如何執行呢?
<html>
<body>
<div>1</div>
<script type="text/javascript" src='foo.js'></script>
<div>test</div>
</body>
</html>
這裡多了一個指令碼下載的過程,JavaScript的下載會阻塞DOM的解析,如何避免這種情況呢?例如利用CDN來加速資源下載,還可以把指令碼壓縮以減小指令碼大小,還可以通過非同步載入的方式來載入指令碼
<script async type="text/javascript" src='foo.js'></script>
<script defer type="text/javascript" src='foo.js'></script>
async的方式是非同步載入指令碼,載入完成之後會立即執行,還是有可能阻塞DOM解析。defer標記的指令碼,需要在DOMContentLoaded事件之前執行,也就是HTML解析完成之後再執行,不會阻塞DOM解析
再看一個同時擁有css樣式和JavaScript指令碼的例子
<html>
<head>
<style src='theme.css'></style>
</head>
<body>
<div>1</div>
<script>
let div1 = document.getElementsByTagName('div')[0]
div1.innerText = 'example' //需要DOM
div1.style.color = 'red' //需要CSSOM
</script>
<div>test</div>
</body>
</html>
上面的例子中,JavaScript指令碼中操作了CSSDOM,所以在執行指令碼內的樣式操作dom之前,需要先執行完CSS樣式,所以如果程式碼裡引入了外部的CSS,必須是先下載CSS樣式表,再去執行JavaScript
結論
JavaScript指令碼會阻塞HTML解析,而外部CSS樣式表又會阻塞JavaScript指令碼的執行
相關文章
- 【瀏覽器】瀏覽器基本工作原理瀏覽器
- 梳理瀏覽器工作原理瀏覽器
- [譯]瀏覽器工作原理探究瀏覽器
- 動畫: 一個瀏覽器是如何工作的?動畫瀏覽器
- JavaScript中的瀏覽器檢測和DOM基礎JavaScript瀏覽器
- 瀏覽器中的JavaScript:文件物件模型與 DOM 操作瀏覽器JavaScript物件模型
- 【瀏覽器】聊聊DOM瀏覽器
- 深入理解瀏覽器工作原理瀏覽器
- 瀏覽器/nodeJS 中的事件環工作原理瀏覽器NodeJS事件
- WebKit Inside: DOM樹的構建WebKitIDE
- Web 應用安全性: 瀏覽器是如何工作的Web瀏覽器
- 瀏覽器架構-原理篇瀏覽器架構
- JavaScript 是如何工作:Shadow DOM 的內部結構 + 如何編寫獨立的元件!JavaScript元件
- 前端效能優化(一)——瀏覽器工作原理前端優化瀏覽器
- Web開發學習筆記——瀏覽器是如何工作的Web筆記瀏覽器
- 【譯】瀏覽器如何工作:在現代web瀏覽器場景的之下瀏覽器Web
- 瀏覽器原理瀏覽器
- JavaScript是如何工作的:使用MutationObserver跟蹤DOM的變化JavaScriptServer
- 構建 EOS 區塊鏈瀏覽器區塊鏈瀏覽器
- 淺談瀏覽器實時構建瀏覽器
- 瀏覽器工作原理及web 效能優化(上)瀏覽器Web優化
- XML DOM 瀏覽器差異概述XML瀏覽器
- 前端都該懂的瀏覽器工作原理,你懂了嗎?前端瀏覽器
- 前端開發者應該明白的瀏覽器工作原理前端瀏覽器
- 瀏覽器是如何解析html的?瀏覽器HTML
- 瀏覽器渲染原理瀏覽器
- 構建 EOS 區塊鏈瀏覽器 API區塊鏈瀏覽器API
- 什麼是DOM?如何構建web頁面Web
- 從程式和執行緒瞭解瀏覽器的工作原理執行緒瀏覽器
- hello world 是如何輸出到瀏覽器的瀏覽器
- ie瀏覽器退役後還能用嗎 ie瀏覽器停止更新服務以後有影響嗎瀏覽器
- 谷歌瀏覽器禁用JavaScript谷歌瀏覽器JavaScript
- JavaScript瀏覽器事件物件JavaScript瀏覽器事件物件
- 中科三方:瀏覽器是如何檢查SSL證書正常工作的?瀏覽器
- 【瀏覽器】渲染原理探究瀏覽器
- 瀏覽器執行原理瀏覽器
- 瀏覽器快取原理瀏覽器快取
- 谷歌瀏覽器審查編輯DOM元素谷歌瀏覽器