都知道瀏覽器和服務端是通過 HTTP 協議進行數據傳輸的,而 HTTP 協議又是純文本協議,那么瀏覽器在得到服務端傳輸過來的 HTML 字符串,是如何解析成真實的 DOM 元素的呢,也就是我們常說的生成 DOM Tree,最近了解到狀態機這樣一個概念,于是就萌生一個想法,實現一個 innerHTML 功能的函數,也算是小小的實踐一下。
函數原型
我們實現一個如下的函數,參數是 DOM 元素和 HTML 字符串,將 HTML 字符串轉換成真實的 DOM 元素且 append 在參數一傳入的 DOM 元素中。
function html(element, htmlString) { // 1. 詞法分析 // 2. 語法分析 // 3. 解釋執行}在上面的注釋我已經注明,這個步驟我們分成三個部分,分別是詞法分析、語法分析和解釋執行。
詞法分析
詞法分析是特別重要且核心的一部分,具體任務就是:把字符流變成 token 流。
詞法分析通常有兩種方案,一種是狀態機,一種是正則表達式,它們是等效的,選擇你喜歡的就好。我們這里選擇狀態機。
首先我們需要確定 token 種類,我們這里不考慮太復雜的情況,因為我們只對原理進行學習,不可能像瀏覽器那樣有強大的容錯能力。除了不考慮容錯之外,對于自閉合節點、注釋、CDATA 節點暫時均不做考慮。
接下來步入主題,假設我們有如下節點信息,我們會分出哪些 token 來呢。
<p class="a" data="js">測試元素</p>
對于上述節點信息,我們可以拆分出如下 token
開始標簽:<p 屬性標簽:class="a" 文本節點:測試元素 結束標簽:</p>狀態機的原理,將整個 HTML 字符串進行遍歷,每次讀取一個字符,都要進行一次決策(下一個字符處于哪個狀態),而且這個決策是和當前狀態有關的,這樣一來,讀取的過程就會得到一個又一個完整的 token,記錄到我們最終需要的 tokens 中。
萬事開頭難,我們首先要確定起初可能處于哪種狀態,也就是確定一個 start 函數,在這之前,對詞法分析類進行簡單的封裝,具體如下
function HTMLLexicalParser(htmlString, tokenHandler) { this.token = []; this.tokens = []; this.htmlString = htmlString this.tokenHandler = tokenHandler}簡單解釋下上面的每個屬性
token:token 的每個字符 tokens:存儲一個個已經得到的 token htmlString:待處理字符串 tokenHandler:token 處理函數,我們每得到一個 token 時,就已經可以進行流式解析我們可以很容易的知道,字符串要么以普通文本開頭,要么以 < 開頭,因此 start 代碼如下
HTMLLexicalParser.prototype.start = function(c) { if(c === '<') { this.token.push(c) return this.tagState } else { return this.textState(c) }}
新聞熱點
疑難解答
圖片精選