vue模板編譯實現原理

模板字符串編譯成語法樹

編譯流程:javascript

  1. 編譯過程當中涉及兩個數組A[],B[]
  2. 解析模板字符串,當匹配到起始標籤,將包含起始標籤信息(名稱、屬性)的對象添加到A數組中,同時去除模板字符串中的匹配部分。
  3. 根據起始標籤信息建立語法樹節點,語法樹節點包含名稱、屬性、父節點、子節點(children[])等信息,設置節點的父節點指向當前父節點(currentParent),將語法樹節點添加到添加到數組B中,同時設置節點爲當前父節點
  4. 繼續解析模板字符串,若是匹配到起始標籤,繼續步驟2。
  5. 若是匹配到結束標籤,找到剛添加到數組B中的節點(數組中的最後一個節點),並從數組B中移除,同時設置數組的最後一個節點爲當前父節點,將找到的節點添加到當前父節點的子節點數組children[]中。
  6. 找到最近添加到數組A中和結束標籤名稱相同的起始標籤,並從數組A中移除。
經過數組 A能夠匹配開始標籤對應的結束標籤。經過數組 B構建層級結構,造成語法樹。構建過程是一個深度遍歷。

對數組AB的操做和棧一致。java

根節點指向第一個添加到數組B的節點,最後返回根節點。node

image

語法樹優化:靜態節點標記

遍歷語法樹,標記語法樹中的靜態節點。根據以前的標記結果,再遍歷標記靜態根節點。在渲染時,對有標記靜態根節點的子樹進行緩存,下次渲染時直接從緩存中讀取。express

怎麼判斷是靜態節點

知足如下一個條件且子節點也都是靜態節點:數組

  1. 文本節點
  2. 有pre屬性的節點
  3. 節點沒有表達式;不是內建組件節點;沒有動態綁定;沒有if或者for,不是有for指令節點的子節點;節點上的屬性都是靜態屬性。
function isStatic (node) {
  if (node.type === 2) { // expression
    return false
  }
  if (node.type === 3) { // text
    return true
  }
  return !!(node.pre || (
    !node.hasBindings && // no dynamic bindings
    !node.if && !node.for && // not v-if or v-for or v-else
    !isBuiltInTag(node.tag) && // not a built-in 不是內建組件slot,component
    isPlatformReservedTag(node.tag) && // not a component 是原生標籤
    !isDirectChildOfTemplateFor(node) &&// 不是有for指令的template標籤的子標籤
    Object.keys(node).every(isStaticKey)// 節點上的每一個屬性都是靜態屬性
  ))
}
怎麼判斷是靜態根節點

節點自己是靜態節點,而且包含子節點,若是隻有一個子節點,該子節點不能是純文本節點(若是不知足這些條件作優化,成本將會超過它帶來的價值)。緩存

// For a node to qualify as a static root, it should have children that
    // are not just static text. Otherwise the cost of hoisting out will
    // outweigh the benefits and it's better off to just always render it fresh.
if (node.static && node.children.length && !(
      node.children.length === 1 &&// 1.靜態節點(子節點都是靜態節點),2. 包含子節點,3. 但又不是隻有一個文本子節點
      node.children[0].type === 3
    )) {
      node.staticRoot = true;
      return
    } else {
      node.staticRoot = false;
    }

語法樹轉化成表達式字符串

遍歷語法樹,根據每一個節點生成表達式字符串,而後拼接或者嵌套這些字符串,最後造成一個和語法樹對應的完整的表達式字符串,渲染時經過with執行字符串中的表達式,生成虛擬節點樹。優化

function generate (
  ast,
  options
) {
  var state = new CodegenState(options);
  var code = ast ? genElement(ast, state) : '_c("div")';// 完整的表達式字符串
  return {
    render: ("with(this){return " + code + "}"),
    staticRenderFns: state.staticRenderFns// 保存了靜態根節點子樹生成的表達式字符串
  }
}

在遍歷過程當中,將有標記靜態根節點的子樹生成的表達式字符串保存到單獨的數組中,完整的表達式字符串保存了靜態子樹表達式字符串在數組中的索引。ui

function genStatic (el, state) {
  el.staticProcessed = true;
  var originalPreState = state.pre;
  if (el.pre) {
    state.pre = el.pre;
  }
  state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}"));
  state.pre = originalPreState;
  return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")")// 保存靜態子樹表達式字符串在數組中的索引
}
相關文章
相關標籤/搜索