vue學習之聊聊模板編譯原理

2023-03-07 22:00:32

什麼是模板編譯?下面本篇文章帶大家聊聊vue中的模板編譯,探討一下模板編譯原理,希望對大家有所幫助!

vue提供了模板語法,允許我們宣告式地描述狀態和DOM之間的繫結關係,比如<p>{{name}}<p>

模板編譯指的是模板將編譯成render函數的過程,渲染函數的作用是每次執行時,會根據最新狀態生成新的vnode

編譯的過程是:模板作為輸入 -> 模板編譯 階段->生成 渲染函數

面試題

  • vue的模板編譯?
  • 模板編譯Compiler中render講解?
  • vue 模板編譯的過程,每一個過程細說一下做了些什麼
  • 模板編譯,誰去解析AST樹 【相關推薦:、】

將模板編譯成渲染函數

在這裡插入圖片描述

  • 解析器:將模板解析為AST(Abstract Syntax Tree 抽象語法樹)
  • 優化器:遍歷AST標記靜態節點,因為靜態節點不可變,不需要為打上標籤的靜態節點建立新的虛擬節點,直接克隆已有的虛擬節點。
  • 程式碼生成器:使用AST生成渲染函數。將AST轉換成程式碼字串。將程式碼字串放入渲染函數中,匯出被外界使用。

案例

在這裡插入圖片描述

1.模板確認

假設如下程式碼,有eltemplaterender$mount

//複雜案例
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)

//腳手架建立的案例
let vue = new Vue({
  render: h => h(App)
}).$mount('#app')
登入後複製

在這裡插入圖片描述

1)渲染到哪個根節點上:判斷有無el屬性,有的話直接獲取el根節點,沒有的話呼叫$mount時去獲取根節點

2)渲染哪個模板到根節點上去:是否呼叫render 函數傳入了模板 render: h => h(App) -> <App></App>

  • 有render:這時候優先執行render函數,render優先順序 > template
  • 無render:
    • 有template:template解析成render函數的所需格式-程式碼字串,並使用呼叫render函數渲染
    • 無template:el根節點的outerHTML解析成render函數的所需格式-程式碼字串,並使用呼叫render函數渲染
      3.渲染的方式:無論什麼情況,最後都統一是要使用render函數渲染

2.解析器-將模板解析成AST

解析器-將模板解析成AST

<div>
  <p>{{name}}</p>
</div>
登入後複製

將上述模板解析成AST後,AST抽象語法樹就是使用JS中的物件來描述一個節點,一個物件表示一個節點。

{
  tag: "div"
  type: 1, //節點型別
  staticRoot: false,
  static: false,
  plain: true,
  parent: undefined, //存放父節點
  attrsList: [],
  attrsMap: {},
  children: [ //存放孩子節點
      {
      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)"
      }]
    }
  ]
}
登入後複製

解析器的工作原理

解析器的原理的是一小段一小段地擷取模板字串,每擷取一小段字串,就會根據擷取出來的字串型別觸發不同的勾點函數,直到模板字串截空停止。然後使用棧來確定層級關係

解析器內部分也分幾個子解析器,如HTML解析器、文字解析器等。

HTML解析器的作用是解析HTML,在解析HTML的過程中不斷觸發各種勾點函數,

  • 開始標籤的勾點函數中可以構建元素型別的節點
  • 文字勾點函數中可以構建文字型別的節點
  • 註釋勾點函數中可以構建註釋型別的節點
  • 結束標籤勾點函數

文字解析器是對HTML解析出來的文字進行二次加工,比如插值語法{{}}

如何確定DOM之間的層級關係?使用棧
在觸發開始標籤的勾點函數時,如果當前標籤不是自閉合標籤,就pushstack
在觸發結束標籤的勾點函數時,就從棧中pop出戰

3.優化器-標記AST中的靜態節點

標記靜態子樹的好處

  • 每次重新渲染時,不需要為靜態子樹建立新虛擬子樹,克隆已存在的靜態子樹
  • 在虛擬DOM中打修補程式(patching)的過程可以跳過 ,靜態子樹是不可變的

優化器的內部實現主要分兩步用遞迴的方式將所有節點新增 static 屬性,true表示是靜態的,false表示不是靜態的。

  • 在AST中找出所有靜態節點並打上標記
    靜態節點:DOM不會發生變化的節點
    通過遞迴的方式從上向下標記靜態節點,如果一個節點被標記為靜態節點,但它的子節點卻被標記為動態節點,就說明該節點不是靜態節點,可以將它改為動態節點。靜態節點的特徵是它的子節點也必須是靜態的。

靜態根節點也是靜態節點

  • **在AST中找出所有靜態根節點並打上標記 **
    靜態根節點:子節點全是靜態節點的節點
    使用遞迴從上向下尋找,在尋找的過程中遇見的第一個靜態節點就為靜態根節點,同時不繼續往下找。

如果一個靜態根節點的子節點只有一個文位元組點或沒有子節點,那麼不會標記成靜態根節點,即使他們是,因為優化成本大於收益

怎麼判斷是否靜態節點?
在將模板字串解析成AST的時候,會根據不同的文字型別設定一個 type

type說明是否時靜態節點
1元素節點進行一些排除
2帶遍歷的動態文位元組點不是
3不帶遍歷的純文位元組點

4.程式碼生成器-將AST轉化成渲染函數中的程式碼字串

程式碼生成器的作用:將AST轉化成渲染函數中的程式碼字串

<div>
  <p>{{name}}</p>
</div>
//生成的render渲染函數
{
  render: `with(this){return _c('div',[_c('p',[_v(_s(name))])])}`
}
//格式化後
with(this){
  return _c(
    'div',
    [
      _c(
        'p',
        [
          _v(_s(name))
        ]
      )
    ]
  )
}
登入後複製

生成程式碼字串是一個遞迴的過程,從頂向下依次處理每一個AST節點。
節點有三種型別,分別對應三種不同的建立方法與別名。

型別建立方法別名
元素節點createElement_c
文位元組點createTextVNode_v
註釋節點createEmptyVNode_e

渲染函數可以生成VNode的原因:渲染函數其實是執行了createElement,而createElement可以建立VNode。

程式碼字串的拼接過程

遞迴AST來生成字串,最先生成根節點,然後在子節點字串生成後,將其拼接在根節點的引數中,子節點的子節點拼接在子節點的引數中,一層層拼接。

(學習視訊分享:、)

以上就是vue學習之聊聊模板編譯原理的詳細內容,更多請關注TW511.COM其它相關文章!