Vue編譯器optimize源碼分析
引言
接上文 parseHTML 函數(shù)源碼解析 chars、end、comment鉤子函數(shù)
上一章節(jié)我們講到通過解析將template轉成AST(抽象語法樹),接下來繼續(xù)對模型樹優(yōu)化,進行靜態(tài)標注。那么問題來了,什么是靜態(tài)標注?為什么要靜態(tài)標注。
在源碼的注釋中我們找到了下面這段話:
/** * Goal of the optimizer: walk the generated template AST tree * and detect sub-trees that are purely static, i.e. parts of * the DOM that never needs to change. * * Once we detect these sub-trees, we can: * * 1. Hoist them into constants, so that we no longer need to * create fresh nodes for them on each re-render; * 2. Completely skip them in the patching process. */
- 永遠不需要變化的DOM就是靜態(tài)的。
- 重新渲染時,作為常量,無需創(chuàng)建新節(jié)點;
optimize 源碼之旅
function optimize(root, options) {
if (!root) {
return
}
isStaticKey = genStaticKeysCached(options.staticKeys || '');
isPlatformReservedTag = options.isReservedTag || no;
// first pass: mark all non-static nodes.
markStatic$1(root);
// second pass: mark static roots.
markStaticRoots(root, false);
}
可以看到源碼并不復雜初始定義了兩個變量。
- isStaticKey 獲取 genStaticKeysCached函數(shù)返回值, 獲取 makeMap (點此查看) 函數(shù)返回值引用 。
- isPlatformReservedTag 獲取編譯器選項 isReservedTag 的引用,檢查給定的字符是否是保留的標簽。
接下來就是兩個重要的方法 markStatic$1 標注靜態(tài)節(jié)點、markStaticRoots 標注靜態(tài)根節(jié)點,我們先來看下 markStatic$1的源碼。
markStatic$1源碼
function markStatic$1(node) {
node.static = isStatic(node);
if (node.type === 1) {
// do not make component slot content static. this avoids
// 1. components not able to mutate slot nodes
// 2. static slot content fails for hot-reloading
if (
!isPlatformReservedTag(node.tag) &&
node.tag !== 'slot' &&
node.attrsMap['inline-template'] == null
) {
return
}
for (var i = 0, l = node.children.length; i < l; i++) {
var child = node.children[i];
markStatic$1(child);
if (!child.static) {
node.static = false;
}
}
if (node.ifConditions) {
for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {
var block = node.ifConditions[i$1].block;
markStatic$1(block);
if (!block.static) {
node.static = false;
}
}
}
}
}
第一步判斷節(jié)點狀態(tài)并標注。
node.static = isStatic(node);
在這給元素描述對象(AST) 擴展了static屬性,通過isStatic方法調用后返回值,確認哪些節(jié)點是靜態(tài)的,哪些是動態(tài)的。
isStatic源碼
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
isPlatformReservedTag(node.tag) && // not a component
!isDirectChildOfTemplateFor(node) &&
Object.keys(node).every(isStaticKey)
))
}
在這判斷node.type的值為2,表示為表達式返回false,node.type的值為3,表示為靜態(tài)文本返回 true 總結:節(jié)點類型為表達式,標注為非靜態(tài);普通文本為靜態(tài)。
上面的很容易理解
復雜點的
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 isPlatformReservedTag(node.tag) && // not a component !isDirectChildOfTemplateFor(node) && Object.keys(node).every(isStaticKey) ))
節(jié)點類型為表達式,標注為非靜態(tài);普通文本為靜態(tài)。
- 無動態(tài)綁定
- 沒有 v-if 和 v-for 指令
- 不是內置的標簽
- 是平臺保留標簽(html和svg標簽)
- 不是 template 標簽的直接子元素并且沒有包含在 for 循環(huán)中
- 結點包含的屬性只能有isStaticKey中指定的幾個
現(xiàn)在你知道了 node.static=isStatic(node) 什么情況為false, 什么情況為true吧!
回歸到markStatic$1
if (node.type === 1) {
// do not make component slot content static. this avoids
// 1. components not able to mutate slot nodes
// 2. static slot content fails for hot-reloading
if (
!isPlatformReservedTag(node.tag) &&
node.tag !== 'slot' &&
node.attrsMap['inline-template'] == null
) {
return
}
for (var i = 0, l = node.children.length; i < l; i++) {
var child = node.children[i];
markStatic$1(child);
if (!child.static) {
node.static = false;
}
}
if (node.ifConditions) {
for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {
var block = node.ifConditions[i$1].block;
markStatic$1(block);
if (!block.static) {
node.static = false;
}
}
}
}
來看看它做了什么,通過一個 if 判斷node.type值為1,對標簽節(jié)點進行處理。如果遇到特殊情況會直接退出去。 什么特殊情況呢?
// do not make component slot content static. this avoids
// 1. components not able to mutate slot nodes
// 2. static slot content fails for hot-reloading
if (
!isPlatformReservedTag(node.tag) &&
node.tag !== 'slot' &&
node.attrsMap['inline-template'] == null
) {
return
}
當遇到了非平臺保留標簽 isPlatformReservedTag(node.tag), 并且標簽節(jié)點是 slot,并且節(jié)點中有inline-template(內聯(lián)模板)三者都滿足此時會終止函數(shù)的執(zhí)行。
如果不滿足條件:
for (var i = 0, l = node.children.length; i < l; i++) {
var child = node.children[i];
markStatic$1(child);
if (!child.static) {
node.static = false;
}
}
通過 node.children 找到子節(jié)點,遞歸子節(jié)點。
if (!child.static) {
node.static = false;
}
子節(jié)點非靜態(tài),該節(jié)點也標注非靜態(tài) 。這塊設計的不太合理有更多好的優(yōu)化方案,在Vue3.0中增加了"動靜分離的策略" 尤大稱之為 Block tree 后續(xù)在跟大家掰扯。
接下來看下 markStaticRoots。
markStaticRoots 源碼
function markStaticRoots(node, isInFor) {
if (node.type === 1) {
if (node.static || node.once) {
node.staticInFor = isInFor;
}
//一個節(jié)點要成為根節(jié)點,那么要滿足以下條件:
//1、靜態(tài)節(jié)點,并且有子節(jié)點
//2、子節(jié)點不能僅為一個文本節(jié)點
if (node.static && node.children.length && !(
node.children.length === 1 &&
node.children[0].type === 3
)) {
node.staticRoot = true;
return
} else {
node.staticRoot = false;
}
//循環(huán)遞歸標記
if (node.children) {
for (var i = 0, l = node.children.length; i < l; i++) {
markStaticRoots(node.children[i], isInFor || !!node.for);
}
}
if (node.ifConditions) {
for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {
markStaticRoots(node.ifConditions[i$1].block, isInFor);
}
}
}
}
一個節(jié)點要成為靜態(tài)根節(jié)點,需要滿足以下條件:
- 自身為靜態(tài)節(jié)點,并且有子節(jié)點
- 子節(jié)點不能僅為一個文本節(jié)點
對于第二個條件,主要考慮到標記靜態(tài)根節(jié)點的受益較小。接下來遞歸循環(huán)其子節(jié)點,循環(huán)標記。
以上就是Vue 編譯器optimize源碼分析的詳細內容,更多關于Vue 編譯器optimize的資料請關注腳本之家其它相關文章!
相關文章
vue自定義js圖片碎片輪播圖切換效果的實現(xiàn)代碼
這篇文章主要介紹了vue自定義js圖片碎片輪播圖切換效果的實現(xiàn)代碼,需要的朋友可以參考下2019-04-04
仿照Element-ui實現(xiàn)一個簡易的$message方法
這篇文章主要介紹了仿照Element-ui實現(xiàn)一個簡易的$message方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09
vue項目webpack中Npm傳遞參數(shù)配置不同域名接口
這篇文章主要介紹了vue項目webpack中Npm傳遞參數(shù)配置不同域名接口,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-06-06
vue中get請求如何傳遞數(shù)組參數(shù)的方法示例
這篇文章主要介紹了vue中get請求如何傳遞數(shù)組參數(shù)的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-11-11
vue下載excel的實現(xiàn)代碼后臺用post方法
這篇文章主要介紹了vue下載excel的實現(xiàn)代碼,后臺用post方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值 ,需要的朋友可以參考下2019-05-05

