上篇文章淺析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提供的思路