[譯]從輸入URL到頁面呈現的超詳細過程——第二步:Tags轉化成DOM的過程

原文連接:Tags to DOM
原文做者:Travis Leithead
譯者:wangds

javascript

這是一個系列文章,分爲四個部分介紹了從輸入URL到頁面呈現的詳細過程css

  • 客戶端從服務器獲取資源(Server to Client)點擊查看
  • 標籤轉化成DOM的過程(tags to DOM)
  • CSS解析的過程(braces to pixels)
  • 編譯執行javascript的過程(var to JIT)

本篇翻譯的是第二部分:Tags to DOMhtml

在上一篇文章中(客戶端從服務器獲取資源),咱們談論了資源是如何從服務器到達客戶端的,同時也闡述了一些關於緩存、同源的概念。本篇文章將聊一聊HTML資源是如何轉化成DOM tree的。java

解析的過程(Parsing)

當瀏覽器得到了資源之後要進行的第一步工做就是解析,整個解析的過程能夠拆分紅以下四個步驟:web

  1. 解碼(encoding)
  2. 預解析(pre-parsing)
  3. 符號化(Tokenization)
  4. 構建樹(tree construction)

1.解碼(encoding)

客戶端接收的內容能夠是HTML文本、圖像等等任何格式的數據。在從服務器到客戶端展現的過程當中,數據通過了以下的過程:canvas

  1. 服務器將數據轉換成bit
  2. bit進行數據傳輸
  3. 客戶端將bit轉化成數據

因此客戶端必須知道數據轉化成bit的格式,以便客戶端將bit反轉成正確的數據。瀏覽器

咱們以HTML舉例來講,HTML的文本格式有不少種,因此服務器在返回數據的時候在響應頭中使用Content-Type告知瀏覽器文本的內容類型,同時在文本文件頭部使用Byte Order Mark告知瀏覽器編碼格式。若是瀏覽器仍然判斷不了解碼方式,瀏覽器還會自行匹配一種解碼格式來處理數據。有時候,解碼格式也會寫在<meta>標籤中。這就致使會出現一種奇葩的現象,瀏覽器先是以本身的方式去解碼文本,而後遇到了標明解碼方式的<meta>標籤。這時候瀏覽器就會從新解析按照<meta>標籤進行解析。緩存

咱們如今常常在HTML中使用的文件格式是UTF-8,那是由於UTF-8能較完整的支持Unicode字符範圍,同時與CSSJavaScript中常見的節字符具備良好的ASCII兼容性。通常瀏覽器默認的解碼格式也是 UTF-8。當解碼出錯的時候,咱們會看到屏幕上所有都是亂碼字符。bash

2.預解析(Pre-parsing/scanning)

完成了解碼之後,瀏覽器會開啓一個預解析的過程,「偷偷獲取」後面須要加載的資源,減小處理的時間。預解析的過程不是一個完整的解析過程。舉例來講,預解析不會處理HTML內元素之間嵌套的父子層級。可是預解析仍是能識別一些特殊的HTML標籤、元素屬性值以及URL。假如咱們的頁面中有個img(以下),預解析就會注意src屬性值,並將獲取這個圖片的請求加到請求隊列中,儘早的將圖片獲取到本地以便顯示。咱們也能夠經過prefetchpreload兩種屬性將須要儘早加載的資源告知於瀏覽器。服務器

<img src="https://somewhere.example.com/​images/​dog.png" alt="">
複製代碼

3.符號化(TOKENIZATION)

符號化是將輸入解析成爲符號,像開始標籤(<)、結束標籤(>)、註釋(//)、文本內容等等。而後這些符號將被送到解析的下一個步驟。完成符號化工做的機器叫作符號識別器( tokenizer),符號識別器是一種狀態機。 咱們以<video controls>的標籤爲例:

  • 狀態機初始狀態是Data State,
  • 當遇到<的時候,狀態變成Tag open state
  • 當解析到字母v的時候,狀態變成Tag name state
  • 當解析到字母c的時候,狀態變成in attribute name state
  • 解析完字母s的時候,狀態變成after attribute name state
  • 解析完>的時候,狀態變成Data state

從解析<video controls>標籤咱們就能夠知道,整個頁面會碰到不少標籤,符號識別器就會不斷的重複狀態變化。(下圖中的箭頭就是狀態機處於的某個位置)

avatar

HTML標準目前爲符號識別器定義了80個獨立的狀態。在解析的過程當中,符號識別器能夠將任何的文本轉化成HTML文檔(即使文本內容不是有效的HTML)。這種彈性的機制使得HTML更加的易於開發,可是也可能致使由於寫錯標籤出現特殊的bug。

對於那些更喜歡使用‘非黑即白’驗證規則的人,瀏覽器內置了一種替代解析機制,可將任何解析錯誤視爲災難性的故障(出現錯誤就會致使沒法呈現內容)。 這種解析模式使用XML規則來處理HTML,咱們能夠經過設置MIME類型的值爲application / xhtml + xml開啓這種解析模式。

瀏覽器能夠同時處理預解析和標記化兩個過程(也就是說這是兩個線程)。

4. 構建樹(tree construction)

在上一步符號化之後,解析器得到這些符號標記,而後以合適的方法建立DOM對象並將這些符號插入到DOM對象中。DOM對象的數據結構是樹狀的,因此這個過程稱爲構造樹(tree construction)。順便說一句,在IE的歷史中,大部分時間裏沒有使用樹結構。

avatar

另外現代瀏覽器爲了兼容舊版本HTML,解析的過程仍是十分複雜的。例如瀏覽器能夠自動閉合標籤。

<p>sincerely<p>The authors</p> //HTML的樣子

<p>sincerely</p><p>The authors</p>//瀏覽器解析出來的樣子
複製代碼

咱們可使用JavaScript去任意修改DOM。假如咱們用JavaScript進行無心義的操做(例如在video元素內插入一個table單元格),渲染引擎能夠甄別出這些操做不合規,也不進行渲染(可是這個過程渲染引擎也進行工做只是不展現)。

由於JavaScript能夠在DOM中添加內容。因此遇到JavaScript文件的時候,DOM將中止解析;若是JavaScript文件內調用了document.writeAPI,解析器將從新開始解析過程。

事件(Events)

當整個解析的過程完成之後,瀏覽器會經過DOMContentLoaded事件來通知DOM解析完成。事件至關於一套廣播系統用來通知和監聽瀏覽器。除了DOMContentLoaded事件,還有load事件(表示全部資源已經加載完成,包括圖片、視頻、CSS等等)、unload事件表示界面即將關閉、鼠標事件鍵盤事件等等。

瀏覽器在DOM對象中還會建立一個事件對象,該對象包含大量有用的信息(如屏幕中的座標、按下的按鍵等等)。當JavaScript觸發事件的時候,就會同時產生事件對象。

在目標元素上觸發事件的時候,須要從DOM樹的根元素開始向子元素查找,這個過程俗稱事件捕捉階段。到達目標元素之後,還要逐級向上返回到根元素上,這個過程俗稱事件冒泡階段。

avatar

事件是能夠進行關閉的,咱們能夠在事件捕獲或者傳播的過程當中選擇關閉,例如在form的提交過程當中判斷字符串爲空則關閉提交。

DOM

解析器解析出來的元素能夠包含一些基本的交互功能(<video>能播放視頻、<button>按鈕等),可是這些基本的交互功能還不能知足咱們的需求,咱們須要使用cssJavaScript將頁面展現的更加豐富多彩,而DOM就是HTML元素與其餘資源交互的橋樑。

元素的接口

在解析器將元素放入DOM樹以前,解析器會根據不一樣元素的名稱賦予元素不一樣的接口功能。

下面是一些通用的功能:

  • 能夠訪問元素內的子元素集合
  • 能夠搜索子元素的屬性和方法
  • 最重要的是,能夠不經過解析器添加新的元素到DOM

<table>元素具備查找刪除表內全部單元格的能力;<canvas>具備描繪線條、圖形的能力。可是想要調用這個接口仍是須要JavaScript來操做。

每當咱們使用JavaScript操做DOM的時候,將會觸發瀏覽器的一些連鎖反應,這些反應是爲了讓更改後的頁面更快的呈如今屏幕上。例如:

  • 用數字表明通用的元素名稱和屬性,瀏覽器用使用哈希表進行快速識別這些數字
  • 將頻繁變動的子元素進行緩存,方便子元素快速迭代
  • sub-tree的跟蹤變化降到最低,避免‘污染’整個DOM

DOM中的HTML元素是頁面展現的基礎,DOM的能力也不只僅侷限於上面咱們提到的功能。其餘諸如訪問儲存系統(瀏覽器中的緩存)、設備功能(藍牙、定位)、以及3D圖形繪製等等功能。隨着瀏覽器的不斷進步和web標準的不斷實施,DOM的功能只會愈來愈強大,本文只涉及了一部分而已。

結束語

以上就包含了標籤轉化成DOM的過程。下一篇文章咱們將聊聊CSS解析的過程。

限於本人水平有限,文中若有錯誤感謝您指正。

相關文章
相關標籤/搜索