Vue編譯器optimize源碼分析
引言
接上文 parseHTML 函數(shù)源碼解析 chars、end、comment鉤子函數(shù)
上一章節(jié)我們講到通過解析將template轉(zhuǎn)成AST(抽象語法樹),接下來繼續(xù)對(duì)模型樹優(yōu)化,進(jìn)行靜態(tài)標(biāo)注。那么問題來了,什么是靜態(tài)標(biāo)注?為什么要靜態(tài)標(biāo)注。
在源碼的注釋中我們找到了下面這段話:
/** * 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. */
- 永遠(yuǎn)不需要變化的DOM就是靜態(tài)的。
- 重新渲染時(shí),作為常量,無需創(chuàng)建新節(jié)點(diǎn);
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); }
可以看到源碼并不復(fù)雜初始定義了兩個(gè)變量。
- isStaticKey 獲取 genStaticKeysCached函數(shù)返回值, 獲取 makeMap (點(diǎn)此查看) 函數(shù)返回值引用 。
- isPlatformReservedTag 獲取編譯器選項(xiàng) isReservedTag 的引用,檢查給定的字符是否是保留的標(biāo)簽。
接下來就是兩個(gè)重要的方法 markStatic$1 標(biāo)注靜態(tài)節(jié)點(diǎn)、markStaticRoots 標(biāo)注靜態(tài)根節(jié)點(diǎn),我們先來看下 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é)點(diǎn)狀態(tài)并標(biāo)注。
node.static = isStatic(node);
在這給元素描述對(duì)象(AST) 擴(kuò)展了static屬性,通過isStatic方法調(diào)用后返回值,確認(rèn)哪些節(jié)點(diǎn)是靜態(tài)的,哪些是動(dòng)態(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,表示為表達(dá)式返回false,node.type的值為3,表示為靜態(tài)文本返回 true 總結(jié):節(jié)點(diǎn)類型為表達(dá)式,標(biāo)注為非靜態(tài);普通文本為靜態(tài)。
上面的很容易理解
復(fù)雜點(diǎn)的
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é)點(diǎn)類型為表達(dá)式,標(biāo)注為非靜態(tài);普通文本為靜態(tài)。
- 無動(dòng)態(tài)綁定
- 沒有 v-if 和 v-for 指令
- 不是內(nèi)置的標(biāo)簽
- 是平臺(tái)保留標(biāo)簽(html和svg標(biāo)簽)
- 不是 template 標(biāo)簽的直接子元素并且沒有包含在 for 循環(huán)中
- 結(jié)點(diǎn)包含的屬性只能有isStaticKey中指定的幾個(gè)
現(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; } } } }
來看看它做了什么,通過一個(gè) if 判斷node.type值為1,對(duì)標(biāo)簽節(jié)點(diǎn)進(jìn)行處理。如果遇到特殊情況會(huì)直接退出去。 什么特殊情況呢?
// 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 }
當(dāng)遇到了非平臺(tái)保留標(biāo)簽 isPlatformReservedTag(node.tag), 并且標(biāo)簽節(jié)點(diǎn)是 slot,并且節(jié)點(diǎn)中有inline-template(內(nèi)聯(lián)模板)三者都滿足此時(shí)會(huì)終止函數(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é)點(diǎn),遞歸子節(jié)點(diǎn)。
if (!child.static) { node.static = false; }
子節(jié)點(diǎn)非靜態(tài),該節(jié)點(diǎn)也標(biāo)注非靜態(tài) 。這塊設(shè)計(jì)的不太合理有更多好的優(yōu)化方案,在Vue3.0中增加了"動(dòng)靜分離的策略" 尤大稱之為 Block tree 后續(xù)在跟大家掰扯。
接下來看下 markStaticRoots。
markStaticRoots 源碼
function markStaticRoots(node, isInFor) { if (node.type === 1) { if (node.static || node.once) { node.staticInFor = isInFor; } //一個(gè)節(jié)點(diǎn)要成為根節(jié)點(diǎn),那么要滿足以下條件: //1、靜態(tài)節(jié)點(diǎn),并且有子節(jié)點(diǎn) //2、子節(jié)點(diǎn)不能僅為一個(gè)文本節(jié)點(diǎn) 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)遞歸標(biāo)記 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); } } } }
一個(gè)節(jié)點(diǎn)要成為靜態(tài)根節(jié)點(diǎn),需要滿足以下條件:
- 自身為靜態(tài)節(jié)點(diǎn),并且有子節(jié)點(diǎn)
- 子節(jié)點(diǎn)不能僅為一個(gè)文本節(jié)點(diǎn)
對(duì)于第二個(gè)條件,主要考慮到標(biāo)記靜態(tài)根節(jié)點(diǎn)的受益較小。接下來遞歸循環(huán)其子節(jié)點(diǎn),循環(huán)標(biāo)記。
以上就是Vue 編譯器optimize源碼分析的詳細(xì)內(nèi)容,更多關(guān)于Vue 編譯器optimize的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue3+ant?design的form數(shù)組表單校驗(yàn)方法
這篇文章主要介紹了vue3+ant?design的form數(shù)組表單,如何校驗(yàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-09-09vue使用swiper插件實(shí)現(xiàn)垂直輪播圖
這篇文章主要介紹了vue使用swiper插件實(shí)現(xiàn)垂直輪播圖,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-01-01案例實(shí)操vue事件修飾符帶你快速了解與應(yīng)用
這篇文章主要介紹了vue常見的事件修飾符,在平時(shí)無論是面試還是學(xué)習(xí)工作中都會(huì)經(jīng)常遇到的,本文就帶你快速上手,需要的朋友可以參考下2023-03-03一篇文章帶你了解vue.js的事件循環(huán)機(jī)制
這篇文章主要為大家詳細(xì)介紹了vue.js事件循環(huán)機(jī)制,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03