HTML代碼是如何被解析成瀏覽器中的DOM對象的

做者:殷榮檜@騰訊javascript

目錄:

1、代碼如何被解析成DOM對象css

2、節點的方法是如何添加到DOM上的html

3、簡單模仿瀏覽器掛載DOM方法前端

4、看JSDom源碼的疑問java

5、瞭解元素的實例化node

你也能夠在個人Github查看這篇文章,歡迎Star.有什麼問題能夠在Github中評論。git

1、代碼如何被解析成DOM對象github

       就以以下的一行代碼爲例,開始探索代碼解析的歷程,先提出第一個疑問,這三行代碼,是怎樣解析成DOM對象的?web

// 代碼以下
<div id="exampleId">
    <span>hello world</span>
</div>
複製代碼

DOM對象以下:正則表達式

       截圖中生成AST的連接,你能夠去點擊嘗試修改HTML代碼生成抽象語法樹試試。這是瀏覽器拿到咱們所寫的HTML文本代碼第一步所要完成的事,由於文本的格式是不便於操做的,好比如今要去一個div節點的屬性id的內容(exampleId),你說怎麼取,估計你能想到用正則表達式,可是不能就靠正則表達式過日子啊,要取的屬性各類各樣,還有自定義的屬性要取,這時候正則表達式表示也頂不住了。只有把文本的內容生成必定的數據結構才方便對其進行操做(如這邊生成的AST抽象語法樹)。這樣經過相似document.html.body.div.attrs.id就能夠成功取到。

       若是你對瀏覽器將HTML生成抽象語法樹的細節感興趣,看chrome源代碼幾乎是不太可行的,你能夠查看這樣一個GitHub倉庫:parse5,這個是用JS實現的,方便查看,你甚至還能夠查看一個正則表達式版本的,也就是我前面提到的啥都用正則表達式來處理的,GitHub倉庫:html-parse-stringify

2、節點的方法是如何添加到DOM上的

       解決了第一個問題,產生了一個簡單的抽象語法樹,接下來問題又來了,平時咱們用的document.getElementByTagName在咱們這個抽象語法樹上並無,咱們只有簡單的HTML結點的屬性nodeName,tagName之類的,並無這些方法,那麼這些獲取節點的方法又是從哪來的呢?

注:在chrome的控制檯中直接輸入document或使用console.log(document)是無法查看document屬性的,
須要使用console.dir(document)來查看其屬性。查看的屬性以下圖所示:
複製代碼

       打印出瀏覽器的docuemnt對象後,咱們在原型鏈上向下翻了好幾層,終於找到了咱們想要找的getElementByTagName,是掛載在Document這樣的一個原型鏈處,以下圖所示。爲何又來一個Document,和咱們以前的console.dir(document)中的document有什麼不一樣?

       到了這邊就須要搬出咱們的W3C標準來了,爲何打印出來的document對象上有那麼多的屬性,有那麼長的原型鏈?由於這些都是W3C標準規定的,就以一個上圖中的屬性字段URL爲例,都是W3C規定的Docuemnt上須要有哪些屬性,能夠點擊這裏查看所定義的屬性,查看這裏。能夠看出document是一個相似與Document的實例,實際上他們中間只是又隔了一層HTMLDocument。

       那麼原型鏈又是如何定義的呢,在W3C標準中能夠看到是經過接口繼承來實現的,你能夠在標準中查到以下字樣:

interface Document : Node 
複製代碼

       這就說明Document是繼承自Node接口的。這就是原型鏈的由來,咱們把原型鏈從頂端到低端都畫出來,大概就是下面這樣了。

image

       從這裏咱們就能夠看出,一個Document是一個W3C定義好的接口,另外一個是HTMLDocument的實例化。

       接下來咱們再一塊兒看看,這條原型鏈上每一個節點的定義:

HTMLDocument接口的定義
現行標準:https://html.spec.whatwg.org/multipage/window-object.html#htmldocument
MDN:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLDocument

Document接口的定義
現行標準:https://dom.spec.whatwg.org/#interface-document
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/Document

Node接口的定義
現行標準:https://dom.spec.whatwg.org/#interface-node
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/Node

EventTarget接口的定義
現行標準:https://dom.spec.whatwg.org/#interface-eventtarget
MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget
複製代碼

3、簡單模仿瀏覽器掛載DOM方法

       看了這些標準以後,你是否是想看看瀏覽器是如何將這些標準中定義的各類屬性及方法掛載到咱們剛纔經過parse5生成的簡單的AST(抽象語法樹)上的。仍是以前說的,看Chrome源代碼不太現實,做爲前端工程師最好的就是看看有沒有JS實現版本的,恭喜,找到了一個JSDom,雖然和Chrome源代碼實現機理不可能100%類似,當原理應該相差很少。接下來就一塊兒看看getElementByTagName這個方法是怎麼掛載到咱們的AST上去的。

       在JSDom這裏找到了whatwg接口的定義,和咱們看到的標準中的定義相差無幾。這邊是JSDom中給出的定義文件。接下來看看JSDom的實現

image

       能夠看出其大概原理就是遵照w3c的標準將所須要的方法掛載到prototype上去了。若是在咱們生成的AST樹上掛載,大概就是這樣

ASTTree.prototype.getElementsByTagName = function(tagName) {
    // 以下代碼只是爲說明大體含義,並不能正常運行
    return this.ASTTree.html.body.childNodes.forEach((child) => {
         child.tagName = tagName;
   })
}
複製代碼

       這樣你要根據TagName獲取到對應的節點,就能夠很方便的使用已經封裝好的這個getElementsByTagName了,這樣就基本有了咱們的DOM樹的雛形了。

4、看JSDom源碼的疑問

       其實當你看JSDom代碼和w3c標準的時候,可能會遇到一些疑惑,這裏我把我本身遇到的幾個疑惑耗時較長的兩個個說明一下,避免你也踩坑。

       第一個疑問是爲何在chrome devtools中用console.dir(document)中有好多的事件,如onauxclick、 onblur、onabort這些在jsdom對於document實現的文檔中都沒有。

後來才發現,原來是經過使用Mixin來實現的,這些在標準中也都有定義

// 具體在jsdom中的實現
mixin(DocumentImpl.prototype, GlobalEventHandlersImpl.prototype);
複製代碼
// 標準中的定義
Document includes GlobalEventHandlers;
複製代碼

       第二個疑問就是,都說屬性也是節點,好比id,name,style都應該有nodeType纔是。可是爲何沒有nodeType呢?

       仍是才疏學淺,只能到stackoverflow上去請教,獲得了以下的答案:

       我在瀏覽器的控制檯中試了一下,的確如此,以下圖所示,說明屬性Attr也的確是一個節點,可是從查找到的信息綜合來看,接下來的標準中會取消這一點,Attr將再也不是節點。

5、瞭解元素的實例化

       以上只是大體瞭解了瀏覽器document實例化的造成過程,趁熱打鐵,把很類似的元素的實例化也瞭解一下。不懂元素的實例化不要緊,來看個例子就瞭解。打開devtools,在Element的Tab中隨意選中一個節點,而後選擇右側的Properties的Tab,就能夠看到這個元素實例的原型鏈。

image
上圖中的div#wrapper就是對 HTMLDivElement的實例化。相似於瀏覽器中的document是對HTMLDocument的實例化是一個道理。對比上面畫出的原型鏈圖,這邊也能夠畫出一個:

image

       每一個原型鏈節點均可以在w3c標準或者MDN中找到定義,實現的過程也和上面的AST上添加方法相似,這些在jsdom中均可以找到。

       至此,咱們基本上搞清楚了咱們平時爲何寫一段文本的HTML代碼,到了瀏覽器中就有個document對象可用,就能夠有好多相似onclick,getElementBy....能夠用。經過getElementById獲取到某一個節點後又會有innerHTML,outerHTML等可使用。這些原來都是根據代碼創建AST後在樹上根據W3C標準添加上了各類方法就造成了你的document,和element等等。

最後,你若是在看文章,或者學習探索DOM的過程當中有什麼疑問,均可以在github,或直接在下方評論。我有時間了就搞一搞,搞出名堂了我再補充到文章中。

參考文章:

How the browser renders HTML & CSS

An Introduction and Guide to the CSS Object Model (CSSOM)

Attributes and properties Provide location info for the attributes

Pointer Events

DOM

相關文章
相關標籤/搜索