首頁 >web前端 >Vue.js >vue學習之聊模板編譯原理

vue學習之聊模板編譯原理

青灯夜游
青灯夜游轉載
2023-03-07 19:01:001527瀏覽

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

vue學習之聊模板編譯原理

vue提供了範本語法,讓我們可以宣告式地描述狀態和DOM之間的綁定關係,例如<p>{{name}} </p> <p></p>

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

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

面試題

  • vue的模板編譯?
  • 模板編譯Compiler中render講解?
  • vue 模板編譯的過程,每一個過程細說一下做了些什麼
  • 模板編譯,誰去解析AST樹【相關推薦:vuejs影片教學web前端開發

將範本編譯成渲染函數

vue學習之聊模板編譯原理

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

案例

vue學習之聊模板編譯原理

1.範本確認

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

//复杂案例
let vue = new Vue({
    el: &#39;#app&#39;,
    data() {
        return {
            a: 1,
            b: [1]
        }
    },
    render(h) {
        return h(&#39;div&#39;, { id: &#39;hhh&#39; }, &#39;hello&#39;)
    },
    template: `<div id=&#39;hhh&#39; style="aa:1;bb:2"><a>{{xxx}}{{ccc}}</a></div>`
}).$mount(&#39;#app&#39;)

console.log(vue)

//脚手架创建的案例
let vue = new Vue({
  render: h => h(App)
}).$mount(&#39;#app&#39;)

vue學習之聊模板編譯原理

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来生成字符串,最先生成根节点,然后在子节点字符串生成后,将其拼接在根节点的参数中,子节点的子节点拼接在子节点的参数中,一层层拼接。

(学习视频分享:vuejs入门教程编程基础视频

以上是vue學習之聊模板編譯原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除