編譯器這是什麼? 簡單的一點是將源**轉換為目標**,詳細的點是將用人們易於編寫、閱讀和維護的高階計算機語言編寫的源**程式翻譯成由計算機解釋和執行的低階機器語言的程式。
Vue 的編譯器大致分為三個階段,即詞法分析>句法分析>生成。 詞法分析階段大致是將字串模板解析為標記,句法分析在詞法分析的基礎上生成AST,**生成根據AST**生成最終。 在本文中,我們將簡要分析詞彙分析的過程。
在源端(src 編譯器索引。js)由這樣的句子**,包含parse
ast
關鍵詞,你知道的parse
該函式用於解析模板字串並生成它ast
目標。
const ast = parse(template.trim(),options)
發現parse
函式,發現在內部呼叫parsehtml
實際上parsehtml
該函式用於詞法分析。 而parse
函式最終是在詞法分析的基礎上生成的ast
/** convert html string to ast.* export function parse ( parse 函式是在詞彙分析的基礎上做句法分析,生成乙個 AST 的函式。 template: string, options: compileroptions): astelement | void .
那我們去看看吧parsehtml
如何讀取字元流並逐步解析模板字串。
export function parsehtml (html, options) if (textend < 0) if (options.chars &&text) else,因為 while 迴圈會呼叫 advance 來更新 html 如果上面的處理等於 2,則表示 html 在通過迴圈的 ** 後沒有變化,此時的 html 字串被視為純文字 如果 (html === last) 整個字串被視為文字。") break } 呼叫 parseendtag 函式 parseendtag() advance 函式刪除解析字串 function advance (n) parsestarttag 函式 parsestarttag 函式 parsestarttag () handlestarttag 函式處理 parsestarttag 結果 function handlestarttag (match) parseendtag 函式 parseendtag (tagname, start, end) }
從上面可以看出,當陣列為空或標籤為純文字標籤(style、script、textarea)時,得到
有三種型別的情況,其中第一次出現在字串中。
1. 在 textend === 0(出現在第乙個位置),以 comment 節點和 start 標籤為例,在內部簡單說明如何處理 textend === 0 的情況。
注釋節點。
if (comment.test(html)) advance(commentend + 3) 呼叫 advance 函式傳入解析字串的結束位置,刪除已經處理過的 HTML,將 HTML 變數更新為剩餘的未處理字串,將 indexd 值更新為 commentend + 3(讀取 html 字串的地方) 繼續跳出這個迴圈 開始下乙個迴圈,再次開始解析 Process。 }
“開始”選項卡。
const starttagmatch = parsestarttag() 呼叫 parsestarttag 函式,並獲取其返回值,如果有返回值,則表示 start 標籤解析成功,確實是 start 標籤 if (starttagmatch) continue}function parsestarttag ()advance(start[0].長度)這裡傳入 tagname 標籤的長度並呼叫高階函式 let end, attr while 迴圈執行的條件是它與開始標籤的結束部分不匹配,並且它與 start 標籤中的屬性 while (!) 匹配end = html.match(starttagclose)) attr = html.match(屬性)))if (end) handlestarttag (match) if (canbeleftopentag(tagname) &lasttag === tagname) const unary = isunarytag(tagname) |unaryslash 確定開始標記是否為一元標記 const l = matchattrs.長度 const attrs = new array(l) for 迴圈用於格式匹配attrs 陣列並將格式化資料儲存在常量 attrs 中 for (let i = 0; i < l; i++)if (args[4] === '') if (args[5] === '') const value = args[3] |args[4] |args[5] |''attrs[i] = attrs 是乙個陣列 } if (!一元) )lasttag = tagname lasttag 變數儲存堆疊頂部的元素 update lasttag variable } if (options.)。start)
2. 在 textend 的情況下 >= 0.
此段落處理第乙個字元<但標籤未成功匹配的字串,或者第乙個字元未<的字串,let text, rest, nextif (textend >= 0) text = htmlsubstring(0, textend) 現在保證文字是純文字 advance(textend)}if (options.)。chars &&text)
3. 在 textend <= 0 的情況下,整個 HTML 字串將作為文字進行處理。
if (textend < 0)
上面的分析是針對最新的標籤是非純文字標籤的情況,那麼如何處理純文字標籤呢? 純文字標籤包括 script 標籤、style 標籤和 textarea 標籤。
let endtaglength = 0 用於儲存純文字標籤,封閉標籤的字元長度 const stackedtag = lasttagtolowercase()const restackedtag = recache[stackedtag] |recache[stackedtag] = new regexp('([\s\\s]*?', 'i'restackedtag 的目的是匹配純文字標籤和結束標籤的內容 ** 使用常規的 restackedtag 來匹配字串 html 並將其替換為空字串,常量 rest 將儲存剩餘字元 const rest = htmlreplace(restackedtag, function (all, text, endtag) if (shouldignorefirstnewline(stackedtag, text)) if (options.chars) return ''將正規表示式替換為與''})index += html.length - rest.length;結束標記位置是 htmllength - 剩餘字串長度 html = rest update html 以啟動新的 while 迴圈 parseendtag(stackedtag, index - endtaglength, index)。
看完上面的段落,我才知道parseendtag
該函式尚未被分析,因此根據名稱,它應該使用結束標記進行處理。
有三種方法可以呼叫 parseendtag() 來處理堆疊中剩餘的未處理標籤。 parseEndTag(TagName) ParseEndTag (TagName, Start, End) 函式 ParseEndTag (TagName, Start, End) 查詢最接近的相同型別的開啟標籤 堆疊閃回 查詢與結束標籤對應的開始標籤 if ( tagname) else if (pos >= 0) >沒有匹配的結束標籤。` if (options.end) // remove the open elements from the stack stack.length = pos 當 tagname 傳入以刪除 pos 後的元素時 tagname 在 pos 0 中傳遞時相當於清空堆疊 lasttag = pos &&&stack[pos - 1]。tag update top of stack element } else if (lowercasedtagname ===.)'br') else if (lowercasedtagname === 'p') if (options.end) pos< 遇到其他缺少開始標籤,忽略結束標籤的情況}
在詞法分析過程中,可以通過讀取字元流並用常規程式碼逐點解析字串來實現,直到解析完整個字串。 每當遇到特定的令牌時,都會呼叫相應的鉤子函式,並傳遞有用的引數。 再parse
基於這些引數生成函式ast