淺談Vue2.0模板渲染

Vue 2.0 中模板渲染與 Vue 1.0 完全不同,1.0 中采用的 DocumentFragment,而 2.0 中借鑒 React 的 Virtual DOM。基于 Virtual DOM,2.0 還可以支持服務端渲染(SSR),也支持 JSX 語法。

相關概念介紹

AST (abstract syntax tree)

AST即抽象語法樹,是源代碼的抽象語法結構的樹狀表現形式。Vue在mount的過程中,template會被編譯成AST語法樹。
Vue中的AST數據結構定義如下:

declare type ASTNode = ASTElement | ASTText | ASTExpression 
 
declare type ASTElement = { // 有關元素的一些定義 
 
  type: 1; 
 
  tag: string; 
 
  attrsList: Array{ name: string; value: string }>; 
 
  attrsMap: { [key: string]: string | null }; 
 
  parent: ASTElement | void; 
 
  children: ArrayASTNode>; 
 
  //...... 
 
} 
 
declare type ASTExpression = { 
 
  type: 2; 
 
  expression: string; 
 
  text: string; 
 
  static?: boolean; 
 
} 
 
declare type ASTText = { 
 
  type: 3; 
 
  text: string; 
 
  static?: boolean; 
 
}  

可以看到Vue中的AST數據結構有三種類型,以type區分:

  • ASTElement 標簽
  • ASTExpression 包含字面量表達式的文本節點
  • ASTText 普通文本節點或注釋節點

Virtual DOM - 輕量級的模擬DOM結構

  • 結構為有序的二叉樹
  • 與真實DOM樹每個位置的屬性一一對應
  • 某一時刻真實DOM狀態的內存映射

VNode - Vue中的VDOM對象

數據結構定義:

constructor { 
 
  this.tag = tag   //元素標簽 
 
  this.data = data  //屬性 
 
  this.children = children  //子元素列表 
 
  this.text = text 
 
  this.elm = elm  //對應的真實 DOM 元素 
 
  this.ns = undefined 
 
  this.context = context 
 
  this.functionalContext = undefined 
 
  this.key = data && data.key 
 
  this.componentOptions = componentOptions 
 
  this.componentInstance = undefined 
 
  this.parent = undefined 
 
  this.raw = false 
 
  this.isStatic = false //是否被標記為靜態節點
 
  this.isRootInsert = true 
 
  this.isComment = false 
 
  this.isCloned = false 
 
  this.isOnce = false 
 
}  

為什么使用VDOM?

  • 創建真實DOM的代價高
    真實的DOM節點實現的屬性很多,僅一個<div>就有多達200種屬性,而VNode僅僅實現一些必要的屬性,相比起來,創建一個VNode的成本比較低
  • 真實DOM頻繁排版與重繪的效率是相當低的
  • 虛擬DOM進行頻繁修改,然后一次性比較并修改真實DOM中需要改的部分

模板渲染流程解析

模板渲染流程

模板渲染大致上分為圖中的三個階段,其中$mount()函數是下面所有函數的入口函數,所做的工作主要分為三步:

  1. 如果你的option里面沒有自定義render()函數,那么,通過compileToFunctions()將HTML模板編譯成可以生成VNode的render函數
  2. new 一個Watcher()實例,觸發updateComponent() 方法
  3. 生成VNode,經過patch(),把VNode更新到DOM上

模板編譯

模板編譯階段涉及兩個函數,compileToFunctions()compile(),其中compileToFunctions()compile()的入口函數,模板編譯的結果是生成render函數和staticRenderFns函數的字符串,其中staticRenderFns函數包含被標記為靜態節點的 VNode,與后續的diff算法優化相關


在進入compileToFunctions()以后,會先檢查緩存中是否有已經編譯好的結果,如果有結果則直接從緩存中讀取,這樣做防止每次同樣的模板都要進行重復的編譯工作。若無可用緩存,則調用compile(),最后對編譯結果進行緩存


compile()做了三件事情,分別是生成AST、優化靜態內容、生成render函數,對應三個函數:

  • parse ()
    主要功能是將 template字符串解析成 AST,采用了 jQuery 作者 John Resig 的 HTML Parser。前面定義了ASTElement的數據結構,parse 函數就是將template里的結構(指令,屬性,標簽等)轉換為AST形式存進ASTElement中,最后解析生成AST

  • optimize()
    主要功能就是標記靜態節點,獲得AST對象的最大靜態子樹,為后面 patch 過程中對比新舊 VNode 樹形結構做優化。被標記為 static 的節點在后面的 diff 算法中會被直接忽略,不做詳細的比較

最大靜態子樹:
不包含參數data屬性的dom節點,每次data數據改變導致頁面重新渲染的時候,最大靜態子樹不需要重新計算生成

  • generate()
    根據 AST 結構拼接生成 render 函數的字符串,另外還會生成staticRenderFns函數字符串

DOM更新

Vue中的DOM更新是一個響應式工作流程,當數據發現變化后,會執行 Watcher 中的update()函數,update() 函數會執行這個渲染函數,輸出一個新的 VNode 樹形結構的數據。然后再調用patch()函數,拿這個新的 VNode 與舊的 VNode 進行對比,只有發生了變化的節點才會被更新到真實 DOM 樹上


patch.js 就是新舊 VNode 對比的 diff 函數,主要是為了優化dom,通過算法使操作dom的行為降到最低,diff 算法來源于 snabbdom,是 VDOM 思想的核心。snabbdom 的算法為了 DOM 操作跨層級增刪節點較少的這一目標進行優化,它只會在同層級進行, 不會跨層級比較。


總結

  1. VDOM
    VDOM總損耗 = 虛擬DOM增刪改 + diff增刪改 + (較少的節點)排版與重繪
    真實DOM總損耗 = 真實DOM完全增刪改 + (可能較多的節點)排版與重繪
    目的是為了避免頻繁引發大面積的DOM操作,提升性能
  1. 模板編譯
    將 template 轉換為 AST,優化 AST,再將 AST 轉換為 render函數
    目的是為了得到render函數

  2. DOM更新
    render函數通過Watcher與數據產生關聯,在數據發生變化時調用patch函數,
    執行此render函數,生成新VNode,與舊VNode進行diff,最終更新DOM樹

參考鏈接
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • vue2.0和1.0模板渲染的區別 Vue 2.0 中模板渲染與 Vue 1.0 完全不同,1.0 中采用的 Do...
    我是上帝可愛多閱讀 1,314評論 0 4
  • 前幾天想學學Vue中怎么編寫可復用的組件,提到要對Vue的render函數有所了解。可仔細一想,對于Vue的ren...
    kangaroo_v閱讀 116,126評論 13 171
  • 回憶 首先,render函數中手寫h=>h(app),new Vue()實例初始化init()和原來一樣。$mou...
    LoveBugs_King閱讀 2,303評論 1 2
  • 1.vue data屬性里面的getter和setter data的每個屬性都有兩個相對應的get和set屬性。 ...
    GXW_Lyon閱讀 693評論 0 0
  • 回憶 這里我們將對render函數把template轉化成vnode的過程進行介紹。 Vue.prototype....
    LoveBugs_King閱讀 979評論 0 0