什么是模板編譯?下面本篇文章帶大家聊聊vue中的模板編譯,探討一下模板編譯原理,希望對大家有所幫助!
(相關(guān)資料圖)
vue提供了模板語法,允許我們聲明式地描述狀態(tài)和DOM之間的綁定關(guān)系,比如<p>{{name}}<p>。
模板編譯指的是模板將編譯成render函數(shù)的過程,渲染函數(shù)的作用是每次執(zhí)行時,會根據(jù)最新狀態(tài)生成新的vnode。
編譯的過程是:模板作為輸入 -> 模板編譯 階段->生成 渲染函數(shù)
解析器:將模板解析為AST(Abstract Syntax Tree 抽象語法樹)優(yōu)化器:遍歷AST標(biāo)記靜態(tài)節(jié)點,因為靜態(tài)節(jié)點不可變,不需要為打上標(biāo)簽的靜態(tài)節(jié)點創(chuàng)建新的虛擬節(jié)點,直接克隆已有的虛擬節(jié)點。代碼生成器:使用AST生成渲染函數(shù)。將AST轉(zhuǎn)換成代碼字符串。將代碼字符串放入渲染函數(shù)中,導(dǎo)出被外界使用。假設(shè)如下代碼,有el、template、render、$mount
//復(fù)雜案例let vue = new Vue({ el: "#app", data() { return { a: 1, b: [1] } }, render(h) { return h("div", { id: "hhh" }, "hello") }, template: `<div id="hhh" style="aa:1;bb:2"><a>{{xxx}}{{ccc}}</a></div>`}).$mount("#app")console.log(vue)//腳手架創(chuàng)建的案例let vue = new Vue({ render: h => h(App)}).$mount("#app")1)渲染到哪個根節(jié)點上:判斷有無el屬性,有的話直接獲取el根節(jié)點,沒有的話調(diào)用$mount時去獲取根節(jié)點
2)渲染哪個模板到根節(jié)點上去:是否調(diào)用render函數(shù)傳入了模板 render: h => h(App) -> <App></App>
解析器-將模板解析成AST
<div> <p>{{name}}</p></div>將上述模板解析成AST后,AST抽象語法樹就是使用JS中的對象來描述一個節(jié)點,一個對象表示一個節(jié)點。
{ tag: "div" type: 1, //節(jié)點類型 staticRoot: false, static: false, plain: true, parent: undefined, //存放父節(jié)點 attrsList: [], attrsMap: {}, children: [ //存放孩子節(jié)點 { tag: "p" type: 1, staticRoot: false, static: false, plain: true, parent: {tag: "div", ...}, attrsList: [], attrsMap: {}, children: [{ type: 2, text: "{{name}}", static: false, expression: "_s(name)" }] } ]}解析器的原理的是一小段一小段地截取模板字符串,每截取一小段字符串,就會根據(jù)截取出來的字符串類型觸發(fā)不同的鉤子函數(shù),直到模板字符串截空停止。然后使用棧來確定層級關(guān)系
解析器內(nèi)部分也分幾個子解析器,如HTML解析器、文本解析器等。
HTML解析器的作用是解析HTML,在解析HTML的過程中不斷觸發(fā)各種鉤子函數(shù),
開始標(biāo)簽的鉤子函數(shù)中可以構(gòu)建元素類型的節(jié)點文本鉤子函數(shù)中可以構(gòu)建文本類型的節(jié)點注釋鉤子函數(shù)中可以構(gòu)建注釋類型的節(jié)點結(jié)束標(biāo)簽鉤子函數(shù)文本解析器是對HTML解析出來的文本進(jìn)行二次加工,比如插值語法{{}}
如何確定DOM之間的層級關(guān)系?使用棧在觸發(fā)開始標(biāo)簽的鉤子函數(shù)時,如果當(dāng)前標(biāo)簽不是自閉合標(biāo)簽,就push進(jìn)stack。 在觸發(fā)結(jié)束標(biāo)簽的鉤子函數(shù)時,就從棧中pop出戰(zhàn)
標(biāo)記靜態(tài)子樹的好處
每次重新渲染時,不需要為靜態(tài)子樹創(chuàng)建新虛擬子樹,克隆已存在的靜態(tài)子樹在虛擬DOM中打補(bǔ)丁(patching)的過程可以跳過 ,靜態(tài)子樹是不可變的優(yōu)化器的內(nèi)部實現(xiàn)主要分兩步用遞歸的方式將所有節(jié)點添加 static 屬性,true表示是靜態(tài)的,false表示不是靜態(tài)的。
在AST中找出所有靜態(tài)節(jié)點并打上標(biāo)記靜態(tài)節(jié)點:DOM不會發(fā)生變化的節(jié)點 通過遞歸的方式從上向下標(biāo)記靜態(tài)節(jié)點,如果一個節(jié)點被標(biāo)記為靜態(tài)節(jié)點,但它的子節(jié)點卻被標(biāo)記為動態(tài)節(jié)點,就說明該節(jié)點不是靜態(tài)節(jié)點,可以將它改為動態(tài)節(jié)點。靜態(tài)節(jié)點的特征是它的子節(jié)點也必須是靜態(tài)的。**在AST中找出所有靜態(tài)根節(jié)點并打上標(biāo)記 ** 靜態(tài)根節(jié)點:子節(jié)點全是靜態(tài)節(jié)點的節(jié)點 使用遞歸從上向下尋找,在尋找的過程中遇見的第一個靜態(tài)節(jié)點就為靜態(tài)根節(jié)點,同時不繼續(xù)往下找。如果一個靜態(tài)根節(jié)點的子節(jié)點只有一個文本節(jié)點或沒有子節(jié)點,那么不會標(biāo)記成靜態(tài)根節(jié)點,即使他們是,因為優(yōu)化成本大于收益
怎么判斷是否靜態(tài)節(jié)點?在將模板字符串解析成AST的時候,會根據(jù)不同的文本類型設(shè)置一個 type
| type | 說明 | 是否時靜態(tài)節(jié)點 |
|---|---|---|
| 1 | 元素節(jié)點 | 進(jìn)行一些排除 |
| 2 | 帶遍歷的動態(tài)文本節(jié)點 | 不是 |
| 3 | 不帶遍歷的純文本節(jié)點 | 是 |
代碼生成器的作用:將AST轉(zhuǎn)化成渲染函數(shù)中的代碼字符串
<div> <p>{{name}}</p></div>//生成的render渲染函數(shù){ render: `with(this){return _c("div",[_c("p",[_v(_s(name))])])}`}//格式化后with(this){ return _c( "div", [ _c( "p", [ _v(_s(name)) ] ) ] )}生成代碼字符串是一個遞歸的過程,從頂向下依次處理每一個AST節(jié)點。 節(jié)點有三種類型,分別對應(yīng)三種不同的創(chuàng)建方法與別名。
| 類型 | 創(chuàng)建方法 | 別名 |
|---|---|---|
| 元素節(jié)點 | createElement | _c |
| 文本節(jié)點 | createTextVNode | _v |
| 注釋節(jié)點 | createEmptyVNode | _e |
渲染函數(shù)可以生成VNode的原因:渲染函數(shù)其實是執(zhí)行了createElement,而createElement可以創(chuàng)建VNode。
代碼字符串的拼接過程
遞歸AST來生成字符串,最先生成根節(jié)點,然后在子節(jié)點字符串生成后,將其拼接在根節(jié)點的參數(shù)中,子節(jié)點的子節(jié)點拼接在子節(jié)點的參數(shù)中,一層層拼接。
(學(xué)習(xí)視頻分享:vuejs入門教程、編程基礎(chǔ)視頻)
以上就是vue學(xué)習(xí)之聊聊模板編譯原理的詳細(xì)內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!
關(guān)鍵詞: