做者:Uday Hiwarale
譯者:前端小智
來源:medium
點贊再看,養成習慣本文
GitHub
https://github.com/qq44924588... 上已經收錄,更多往期高贊文章的分類,也整理了不少個人文檔,和教程資料。歡迎Star和完善,你們面試能夠參照考點複習,但願咱們一塊兒有點東西。css
進入主話題以前,先羅列一下瀏覽器的主要構成:html
當瀏覽器得到了資源之後要進行的第一步工做就是 HTML 解析,,它由幾個步驟組成:編碼、預解析、標記和構建樹。前端
HTTP 響應主體的有效負載能夠是從HTML文本到圖像數據的任何內容。解析器的第一項工做是找出如何轉制剛剛從服務器接收到的 bit
。git
假設咱們正在處理一個HTML文檔,解碼器必須弄清楚文本文檔是如何被轉換成比特(bit)的,以便反轉這個過程。github
記住,最終即便是文本也會被計算機翻譯成二進制,如上圖所示,在本例中是 ASCII 編碼—定義二進制值,如「01000100」表示字母「D」。web
對於文本存在許多可能的編碼—瀏覽器的工做是找出如何正確地解碼文本。服務器應該經過 Content-Type
提供的信息同時在文本文件頭部使用 Byte Order Mark 告知瀏覽器編碼格式。面試
若是仍然沒法肯定編碼,瀏覽器還會自行匹配一種解碼格式來處理數據。有時候,解碼格式也會寫在 <meta>
標籤中。算法
最壞的狀況是,瀏覽器進行了有根據的猜想,而後開始解析以後發現一個矛盾的 <meta>
標籤。在這些罕見的狀況下,解析器必須從新啓動,丟棄以前解碼的內容。瀏覽器有時必須處理舊的 web內容(使用遺留編碼),許多這樣的系統都支持這一點。數據庫
咱們如今常常在 HTML中使用的文件格式是 UTF-8,那是由於 UTF-8 能較完整的支持Unicode 字符範圍,同時與 CSS、JavaScript 中常見的節字符具備良好的 ASCII 兼容性。通常瀏覽器默認的解碼格式也是 UTF-8。當解碼出錯的時候,咱們會看到屏幕上所有都是亂碼字符。canvas
在執行腳本時,其餘線程會解析文檔的其他部分,找出並加載須要經過網絡加載的其餘資源。經過這種方式,資源能夠在並行鏈接上加載,從而提升整體速度。請注意,預解析器不會修改 DOM 樹,而是將這項工做交由主解析器處理;預解析器只會解析外部資源(例如外部腳本、樣式表和圖片)的引用。
預解析器不是完整的解析器,如,它不理解 HTML 中的嵌套級別或父/子關係。可是,預解析能夠識別特定的 HTML 標籤的名稱和屬性,以及 URL。例如,若是你的 HTML 內容中有一個<img src="https://somewhere.example.com/images/dog.png" alt="">
,預解析將注意到src
屬性,並將獲取這個圖片的請求加到請求隊列中。
請求圖片的速度越快越好,將等待它從網絡到達的時間降到最低。預解析還會注意到 HTML 中的某些顯式請求,好比 preload
和 prefetch
指令,並將它們加入等待隊友中進行處理。
該算法的輸出結果是 HTML 標記。該算法使用狀態機來表示。每個狀態接收來自輸入信息流的一個或多個字符,並根據這些字符更新下一個狀態。當前的標記化狀態和樹結構狀態會影響進入下一狀態的決定。這意味着,即便接收的字符相同,對於下一個正確的狀態也會產生不一樣的結果,具體取決於當前的狀態。該算法至關複雜,沒法在此詳述,因此咱們經過一個簡單的示例來幫助你們理解其原理。
基本示例 - 將下面的 HTML 代碼標記化:
<html> <body> Hello world </body> </html>
初始狀態是數據狀態。遇到字符 <
時,狀態更改成「標記打開狀態」。接收一個 a-z
字符會建立「起始標記」,狀態更改成「標記名稱狀態」。這個狀態會一直保持到接收 >
字符。在此期間接收的每一個字符都會附加到新的標記名稱上。在本例中,咱們建立的標記是 html 標記。
遇到 >
標記時,會發送當前的標記,狀態改回「數據狀態」。<body> 標記也會進行一樣的處理。目前 html 和 body 標記均已發出。如今咱們回到「數據狀態」。接收到 Hello world 中的 H
字符時,將建立併發送字符標記,直到接收 </body>
中的 <
。咱們將爲 Hello world 中的每一個字符都發送一個字符標記。
如今咱們回到「標記打開狀態」。接收下一個輸入字符 /
時,會建立 end tag token 並改成「標記名稱狀態」。咱們會再次保持這個狀態,直到接收 >
。而後將發送新的標記,並回到「數據狀態」。</html> 輸入也會進行一樣的處理。
在建立解析器的同時,也會建立 Document 對象。在樹構建階段,以 Document 爲根節點的 DOM 樹也會不斷進行修改,向其中添加各類元素。標記生成器發送的每一個節點都會由樹構建器進行處理。規範中定義了每一個標記所對應的 DOM 元素,這些元素會在接收到相應的標記時建立。這些元素不只會添加到 DOM 樹中,還會添加到開放元素的堆棧中。此堆棧用於糾正嵌套錯誤和處理未關閉的標記。其算法也能夠用狀態機來描述。這些狀態稱爲「插入模式」
。
在上一步符號化之後,解析器得到這些標記,而後以合適的方法建立 DOM
對象並將這些符號插入到 DOM 對象中。DOM
對象的數據結構是樹狀的,因此這個過程稱爲構造樹(tree construction)。另外,在 IE 的歷史中,大部分時間裏沒有使用樹結構。
在建立解析器的同時,也會建立 Document 對象。在樹構建階段,以 Document 爲根節點的 DOM 樹也會不斷進行修改,向其中添加各類元素。標記生成器發送的每一個節點都會由樹構建器進行處理。
規範中定義了每一個標記所對應的 DOM 元素,這些元素會在接收到相應的標記時建立。這些元素不只會添加到 DOM 樹中,還會添加到開放元素的堆棧中。此堆棧用於糾正嵌套錯誤和處理未關閉的標記。其算法也能夠用狀態機來描述。這些狀態稱爲「插入模式」。
例如,考慮這個 HTML:
<p>sincerely<p>The authors</p>
這樣能夠確保結果樹中的兩個段落對象是兄弟節點,而忽略第二個打開的標籤則與一個段落對象相對。 HTML表多是解析器規則試圖確保表具備適當結構的最複雜的表。
儘管存在全部複雜的解析規則,可是一旦建立了 DOM 樹,全部試圖建立正確 HTML 結構的解析規則就再也不強制執行了。
使用 JavaScript,網頁能夠幾乎以任何方式從新排列 DOM 樹,即便它沒有意義,例如,添加表格單元格做爲 <video>
標籤的子項,渲染系統負責弄清楚如何處理任何先後不一致標籤。
HTML 解析中的另外一個複雜因素是 JavaScript 能夠在解析器執行其工做時添加更多要解析的內容。<script>
標籤包含解析器必須收集的文本,而後發送到腳本引擎進行評估。 當腳本引擎解析並評估腳本文本時,解析器會等待。若是JavaScript文件內調用了 document.writeAPI,解析器將從新開始解析過程。
當解析器完成時,它經過一個名爲 DOMContentLoaded
的事件宣佈完成。事件是內置在瀏覽器中的廣播系統,JavaScript能夠偵聽和響應它。除了 DOMContentLoaded
事件,還有load
事件(表示全部資源已經加載完成,包括圖片、視頻、CSS等等)、unload 事件表示界面即將關閉、鼠標事件鍵盤事件等等。
瀏覽器在 DOM 中建立一個事件對象,並將其打包成有用的狀態信息(例如屏幕上觸摸的位置、按下的按鍵等等),當JavaScript觸發事件的時候,就會同時產生事件對象。
DOM 的樹結構經過容許在樹的任何級別監聽事件(如在樹根、樹葉或二者之間的任何地方)。在目標元素上觸發事件的時候,須要 從DOM 樹的根元素開始向子元素查找,這個過程俗稱事件捕捉階段。到達目標元素之後,還要逐級向上返回到根元素上,這個過程俗稱事件冒泡階段。
還能夠取消一些事件,例如,若是表單沒有正確填寫,則能夠中止表單提交。(提交事件是從<form>
元素觸發的,JavaScript 偵聽器能夠檢查表單,若是字段爲空或無效,還能夠選擇取消事件。)
HTML語言提供了豐富的特性集,遠遠超出瞭解析器處理的標記。解析器構建一個結構,其中的元素包含其餘元素,以及這些元素最初具備什麼狀態(它們的屬性)。結構和狀態的組合足以提供基本渲染和一些交互(例如經過內置控件,如<textarea>,<video>,<button>
等)。 可是若是不添加 CSS 和 JavaScript,網絡將很是枯燥(和靜態)。 DOM 爲 HTML 元素和與 HTML 無關的其餘對象提供了額外的功能層。
在解析器將元素放入DOM樹以前,解析器會根據不一樣元素的名稱賦予元素不一樣的接口功能。些通用特性包括:
對於像 <table>
這樣的特殊元素,該接口包含用於查找表中全部行,列和單元格的其餘特定於表的功能,以及用於從表中刪除和添加行和單元格的快捷方式。 一樣,<canvas>
接口具備繪製線條,形狀,文本和圖像的功能。 使用這些 API 須要 JavaScript 僅僅使用 HTML 標籤是不夠的。
每當咱們使用 JavaScript 操做 DOM 的時候,將會觸發瀏覽器的一些連鎖反應,這些反應是爲了讓更改後的頁面更快的渲染在屏幕上。例如:
sub-tree
的跟蹤變化降到最低,避免‘污染’整個 DOM 樹DOM中的HTML元素及其接口是瀏覽器在屏幕上顯示內容的惟一機制。CSS能夠影響佈局,但僅限於HTML元素中存在的內容。最終,若是你想在屏幕上看到內容,它必須經過做爲樹的一部分的HTML接口來完成。
隨着主要瀏覽器引擎開發和實施新的Web標準,DOM公開的功能不斷增長。然而,DOM的這些「額外」API中的大多數都超出了本文的範圍。
但願這部分對你關於 DOM 解析過程多多少少有點幫助,共進步!
你的點贊是我持續分享好東西的動力,歡迎點贊!
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
原文:https://itnext.io/how-the-bro...
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
https://github.com/qq44924588...
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。