淺析Vue源碼(五)—— $mount中template的編譯--optimize

上篇文章淺析Vue源碼(四)—— $mount中template的編譯--parse,咱們介紹了compile 的 parse部分,至此咱們完成了對一個html字符串模板解析成一個AST語法樹的過程。下一步就是咱們須要經過optimize方法,將AST節點進行靜態節點標記。爲後面 patch 過程當中對比新舊 VNode 樹形結構作優化。被標記爲 static 的節點在後面的 diff 算法中會被直接忽略,不作詳細的比較。html

src/compiler/optimizer.js
複製代碼
export function optimize (root: ?ASTElement, options: CompilerOptions) {
  if (!root) return
  // staticKeys 是那些認爲不會被更改靜態的ast的屬性
  isStaticKey = genStaticKeysCached(options.staticKeys || '')
  isPlatformReservedTag = options.isReservedTag || no
  // first pass: mark all non-static nodes.
  // 第一步 標記 AST 全部靜態節點
  markStatic(root)
  // second pass: mark static roots.
  // 第二步 標記 AST 全部父節點(即子樹根節點)
  markStaticRoots(root, false)
}
複製代碼

首先標記全部靜態節點:vue

function isStatic (node: ASTNode): boolean {
  if (node.type === 2) { // 表達式
    return false
  }
  if (node.type === 3) { // 文本節點
    return true
  }
  // 處理特殊標記
  return !!(node.pre || ( // v-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
    isPlatformReservedTag(node.tag) && // not a component
    !isDirectChildOfTemplateFor(node) &&
    Object.keys(node).every(isStaticKey)
  ))
}
複製代碼

ASTNode 的 type 字段用於標識節點的類型,可查看上一篇的 AST 節點定義:node

type 爲 1 表示元素,git

type 爲 2 表示插值表達式,github

type 爲 3 表示普通文本。算法

能夠看到,在標記 ASTElement 時會依次檢查全部子元素節點的靜態標記,從而得出該元素是否爲 static。上面 markStatic 函數使用的是樹形數據結構的深度優先遍歷算法,使用遞歸實現。 接下來繼續標記靜態樹:bash

function markStaticRoots (node: ASTNode, isInFor: boolean) {
  if (node.type === 1) {
   // 用以標記在v-for內的靜態節點。這個屬性用以告訴renderStatic(_m)對這個節點生成新的key,避免patch error
    if (node.static || node.once) {
      node.staticInFor = isInFor
    }
    // 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 && node.children[0].type === 3 )) { node.staticRoot = true return } else { node.staticRoot = false } if (node.children) { for (let i = 0, l = node.children.length; i < l; i++) { markStaticRoots(node.children[i], isInFor || !!node.for) } } if (node.ifConditions) { for (let i = 1, l = node.ifConditions.length; i < l; i++) { markStaticRoots(node.ifConditions[i].block, isInFor) } } } } 複製代碼

markStaticRoots 函數裏並無什麼特別的地方,僅僅是對靜態節點又作了一層篩選。數據結構

總結

optimizer旨在爲語法樹的節點標上static和staticRoot屬性。 遍歷第一輪,標記static屬性:函數

判斷node是否爲static(有諸多條件) 標記node的children是否爲static,若存在non static子節點,父節點更改成static = false 遍歷第二輪,標記staticRootpost

標記static或節點爲staticRoot,這個節點type === 1(通常是含有tag屬性的節點) 具備v-once指令的節點一樣被標記staticRoot 爲了不過分優化,只有static text爲子節點的節點不被標記爲staticRoot 標記節點children的staticRoot

要是喜歡的話給我一個star,github

感謝muwoo提供的思路

相關文章
相關標籤/搜索