Vue3?源碼解讀靜態(tài)提升詳解
什么是靜態(tài)提升
靜態(tài)提升是Vue3編譯優(yōu)化中的其中一個(gè)優(yōu)化點(diǎn)。所謂的靜態(tài)提升,就是指在編譯器編譯的過(guò)程中,將一些靜態(tài)的節(jié)點(diǎn)或?qū)傩蕴嵘戒秩竞瘮?shù)之外。下面,我們通過(guò)一個(gè)例子來(lái)深入理解什么是靜態(tài)提升。
假設(shè)我們有如下模板:
<div> <p>static text</p> <p>{{ title }}</p> </div>
在沒(méi)有靜態(tài)提升的情況下,它對(duì)應(yīng)的渲染函數(shù)是:
function render() { return (openClock(), createClock('div', null, [ createVNode('p', null, 'static text'), createVNode('p', null, ctx.title, 1 /* TEXT */) ])) }
從上面的代碼中可以看到,在這段虛擬DOM的描述中存在兩個(gè) p 標(biāo)簽,一個(gè)是純靜態(tài)的,而另一個(gè)擁有動(dòng)態(tài)文本。當(dāng)響應(yīng)式數(shù)據(jù) title 的值發(fā)生變化時(shí),整個(gè)渲染函數(shù)會(huì)重新執(zhí)行,并產(chǎn)生新的虛擬DOM樹(shù)。在這個(gè)過(guò)程中存在性能開(kāi)銷(xiāo)的問(wèn)題。原因是純靜態(tài)的虛擬節(jié)點(diǎn)在更新時(shí)也會(huì)被重新創(chuàng)建一次,其實(shí)這是沒(méi)有必要的。因此我們需要使用靜態(tài)提升來(lái)解決這個(gè)問(wèn)題。
所謂的 “靜態(tài)提升”,就是將一些靜態(tài)的節(jié)點(diǎn)或?qū)傩蕴嵘戒秩竞瘮?shù)之外。如下面的代碼所示:
// 把靜態(tài)節(jié)點(diǎn)提升到渲染函數(shù)之外 const hoist1 = createVNode('p', null, 'text') function render() { return (openBlock(), createBlock('div', null, [ hoist1, // 靜態(tài)節(jié)點(diǎn)引用 createVNode('p', null, ctx.title, 1 /* TEXT */) ])) }
可以看到,經(jīng)過(guò)靜態(tài)提升后,在渲染函數(shù)內(nèi)只會(huì)持有對(duì)靜態(tài)節(jié)點(diǎn)的引用。當(dāng)響應(yīng)式數(shù)據(jù)發(fā)生變化,并使得渲染函數(shù)重新執(zhí)行時(shí),并不會(huì)重新創(chuàng)建靜態(tài)的虛擬節(jié)點(diǎn),從而避免了額外的性能開(kāi)銷(xiāo)。
transform 轉(zhuǎn)換器
在模板編譯器將模板編譯為渲染函數(shù)的過(guò)程中,transform 函數(shù)扮演著十分重要的角色。它用來(lái)將模板AST轉(zhuǎn)換為 JavaScript AST。下面,我們來(lái)看看 transform 函數(shù)做了什么。
// packages/compiler-core/src/transform.ts // 將 模板AST 轉(zhuǎn)換為 JavaScript AST export function transform(root: RootNode, options: TransformOptions) { // 創(chuàng)建轉(zhuǎn)換上下文 const context = createTransformContext(root, options) // 遍歷所有節(jié)點(diǎn),執(zhí)行轉(zhuǎn)換 traverseNode(root, context) // 如果編譯選項(xiàng)中打開(kāi)了 hoistStatic 選項(xiàng),則進(jìn)行靜態(tài)提升 if (options.hoistStatic) { hoistStatic(root, context) } // 創(chuàng)建 Block,收集所有動(dòng)態(tài)子節(jié)點(diǎn) if (!options.ssr) { createRootCodegen(root, context) } // finalize meta information // 確定最終的元信息 root.helpers = [...context.helpers.keys()] root.components = [...context.components] root.directives = [...context.directives] root.imports = context.imports root.hoists = context.hoists root.temps = context.temps root.cached = context.cached if (__COMPAT__) { root.filters = [...context.filters!] } }
從上面的源碼中可以看到,transform 函數(shù)的實(shí)現(xiàn)并不復(fù)雜。
- 首先,調(diào)用 createTransformContext 函數(shù)創(chuàng)建了一個(gè)轉(zhuǎn)換上下文對(duì)象,該對(duì)象存儲(chǔ)著轉(zhuǎn)換過(guò)程中的一些上下文數(shù)據(jù)。例如當(dāng)前正在轉(zhuǎn)換的節(jié)點(diǎn) currentNode、當(dāng)前轉(zhuǎn)換節(jié)點(diǎn)的父節(jié)點(diǎn)parent、用于替換當(dāng)前正在轉(zhuǎn)換的節(jié)點(diǎn)的 replaceNode 函數(shù)、用于移除當(dāng)前訪問(wèn)的節(jié)點(diǎn)的 removeNode 函數(shù)等。
- 接著調(diào)用 traverseNode 函數(shù),遞歸遍歷模板AST,將模板AST 轉(zhuǎn)換為 JavaScript AST。
- 然后判斷編譯選項(xiàng)options中是否開(kāi)啟了 hoistStatic,如果是,則進(jìn)行靜態(tài)提升。
- 接下來(lái)則創(chuàng)建 Block,收集模板中的動(dòng)態(tài)子節(jié)點(diǎn)。
- 最后做的事情則是確定最終的元信息。
由于本文主要是介紹靜態(tài)提升,因此我們圍繞靜態(tài)提升的代碼繼續(xù)往下探索,其余部分代碼將在其它文章中介紹。
hoistStatic 靜態(tài)提升
hoistStatic 函數(shù)的源碼如下所示:
// packages/compiler-core/src/transforms/hoistStatic.ts export function hoistStatic(root: RootNode, context: TransformContext) { walk( root, context, // Root node is unfortunately non-hoistable due to potential parent // fallthrough attributes. // 根節(jié)點(diǎn)作為 Block 角色是不能被提升的 isSingleElementRoot(root, root.children[0]) ) }
可以看到,hoistStatic 函數(shù)接收兩個(gè)參數(shù),第一個(gè)參數(shù)是根節(jié)點(diǎn),第二個(gè)參數(shù)是轉(zhuǎn)換上下文。在該函數(shù)中,僅僅只是調(diào)用了 walk 函數(shù)來(lái)實(shí)現(xiàn)靜態(tài)提升。
并且在 walk 函數(shù)中調(diào)用 isSingleElementRoot 函數(shù)來(lái)告知 walk 函數(shù)根節(jié)點(diǎn)是不能提升的,原因是根節(jié)點(diǎn)作為 Block 角色是不可被提升的。
我們接下來(lái)繼續(xù)探究 walk 函數(shù)的源碼。
walk 函數(shù)
靜態(tài)提升的真正實(shí)現(xiàn)邏輯在 walk 函數(shù)內(nèi),其源碼如下所示:
function walk( node: ParentNode, context: TransformContext, // 轉(zhuǎn)換上下文對(duì)象 doNotHoistNode: boolean = false // 節(jié)點(diǎn)是否可以被提升 ) { const { children } = node // 子節(jié)點(diǎn)的數(shù)量 const originalCount = children.length // 可提升節(jié)點(diǎn)的數(shù)量 let hoistedCount = 0 for (let i = 0; i < children.length; i++) { const child = children[i] // only plain elements & text calls are eligible for hoisting. // 只有普通元素和文本才能被提升 if ( child.type === NodeTypes.ELEMENT && child.tagType === ElementTypes.ELEMENT ) { // 如果節(jié)點(diǎn)不能被提升,則將 constantType 賦值為 NOT_CONSTANT 不可被提升的標(biāo)記 // 否則調(diào)用 getConstantType 獲取子節(jié)點(diǎn)的靜態(tài)類(lèi)型:ConstantTypes 定義了子節(jié)點(diǎn)的靜態(tài)類(lèi)型 const constantType = doNotHoistNode ? ConstantTypes.NOT_CONSTANT : getConstantType(child, context) // 如果獲取到的 constantType 枚舉值大于 NOT_CONSTANT if (constantType > ConstantTypes.NOT_CONSTANT) { // 如果節(jié)點(diǎn)可以被提升 if (constantType >= ConstantTypes.CAN_HOIST) { // 則將子節(jié)點(diǎn)的 codegenNode 屬性的 patchFlag 標(biāo)記為 HOISTED ,即可提升 ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``) // 提升節(jié)點(diǎn),將節(jié)點(diǎn)存儲(chǔ)到 轉(zhuǎn)換上下文context 的 hoist 數(shù)組中 child.codegenNode = context.hoist(child.codegenNode!) // 提升節(jié)點(diǎn)數(shù)量自增1 hoistedCount++ continue } } else { // node may contain dynamic children, but its props may be eligible for // hoisting. // 包含動(dòng)態(tài)綁定的節(jié)點(diǎn)本身不會(huì)被提升,該動(dòng)態(tài)節(jié)點(diǎn)上可能存在純靜態(tài)的屬性,靜態(tài)的屬性可以被提升 const codegenNode = child.codegenNode! if (codegenNode.type === NodeTypes.VNODE_CALL) { // 獲取 patchFlag 補(bǔ)丁標(biāo)志 const flag = getPatchFlag(codegenNode) // 如果不存在 patchFlag 補(bǔ)丁標(biāo)志 或者 patchFlag 是文本類(lèi)型 // 并且該節(jié)點(diǎn)的 props 是可以被提升的 if ( (!flag || flag === PatchFlags.NEED_PATCH || flag === PatchFlags.TEXT) && getGeneratedPropsConstantType(child, context) >= ConstantTypes.CAN_HOIST ) { // 獲取節(jié)點(diǎn)的 props,并在轉(zhuǎn)換上下文對(duì)象中執(zhí)行提升操作, // 將被提升的 props 添加到轉(zhuǎn)換上下文context 的 hoist 數(shù)組中 const props = getNodeProps(child) if (props) { codegenNode.props = context.hoist(props) } } // 將節(jié)點(diǎn)的動(dòng)態(tài) props 添加到轉(zhuǎn)換上下文對(duì)象中 if (codegenNode.dynamicProps) { codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps) } } } } else if ( // 如果是節(jié)點(diǎn)類(lèi)型是 TEXT_CALL,并且節(jié)點(diǎn)可以被提升 child.type === NodeTypes.TEXT_CALL && getConstantType(child.content, context) >= ConstantTypes.CAN_HOIST ) { // 提升節(jié)點(diǎn) child.codegenNode = context.hoist(child.codegenNode) hoistedCount++ } // walk further if (child.type === NodeTypes.ELEMENT) { // 如果子節(jié)點(diǎn)的 tagType 是組件,則繼續(xù)遍歷子節(jié)點(diǎn) // 以判斷插槽中的情況 const isComponent = child.tagType === ElementTypes.COMPONENT if (isComponent) { context.scopes.vSlot++ } // 執(zhí)行 walk函數(shù),繼續(xù)判斷插槽中的節(jié)點(diǎn)及節(jié)點(diǎn)屬性是否可以被提升 walk(child, context) if (isComponent) { context.scopes.vSlot-- } } else if (child.type === NodeTypes.FOR) { // Do not hoist v-for single child because it has to be a block // 帶有 v-for 指令的節(jié)點(diǎn)是一個(gè) Block // 如果 v-for 的節(jié)點(diǎn)中只有一個(gè)子節(jié)點(diǎn),則不能被提升 walk(child, context, child.children.length === 1) } else if (child.type === NodeTypes.IF) { // 帶有 v-if 指令的節(jié)點(diǎn)是一個(gè) Block for (let i = 0; i < child.branches.length; i++) { // Do not hoist v-if single child because it has to be a block // 如果只有一個(gè)分支條件,則不進(jìn)行提升 walk( child.branches[i], context, child.branches[i].children.length === 1 ) } } } // 將被提升的節(jié)點(diǎn)序列化,即轉(zhuǎn)換成字符串 if (hoistedCount && context.transformHoist) { context.transformHoist(children, context, node) } // all children were hoisted - the entire children array is hoistable. if ( hoistedCount && hoistedCount === originalCount && node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.ELEMENT && node.codegenNode && node.codegenNode.type === NodeTypes.VNODE_CALL && isArray(node.codegenNode.children) ) { node.codegenNode.children = context.hoist( createArrayExpression(node.codegenNode.children) ) } }
可以看到,walk 函數(shù)的代碼比較常,下面,我們將分步對(duì)其進(jìn)行解析。
1、首先,我們來(lái)看 walk 函數(shù)的函數(shù)簽名,代碼如下:
function walk( node: ParentNode, context: TransformContext, // 轉(zhuǎn)換上下文對(duì)象 doNotHoistNode: boolean = false // 節(jié)點(diǎn)是否可以被提升 )
從函數(shù)簽名中可以知道,walk 函數(shù)接收三個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè) node 節(jié)點(diǎn),第二個(gè)參數(shù)是轉(zhuǎn)換器的轉(zhuǎn)換上下文對(duì)象 context,第三個(gè)參數(shù) doNotHoistNode 是一個(gè)布爾值,用來(lái)判斷傳入節(jié)點(diǎn)的子節(jié)點(diǎn)是否可以被提升。
2、初始化兩個(gè)變量,originalCount:子節(jié)點(diǎn)的數(shù)量;hoistedCount:可提升節(jié)點(diǎn)的數(shù)量,該變量將會(huì)用于判斷被提升的節(jié)點(diǎn)是否可序列化。
const { children } = node // 子節(jié)點(diǎn)的數(shù)量 const originalCount = children.length // 可提升節(jié)點(diǎn)的數(shù)量 let hoistedCount = 0
3、我們來(lái)看 for 循環(huán)語(yǔ)句里的第一個(gè) if 語(yǔ)句分支,這里處理的是普通元素和靜態(tài)文本被提升的情況。
// only plain elements & text calls are eligible for hoisting. // 只有普通元素和文本才能被提升 if ( child.type === NodeTypes.ELEMENT && child.tagType === ElementTypes.ELEMENT ) { // 如果節(jié)點(diǎn)不能被提升,則將 constantType 賦值為 NOT_CONSTANT 不可被提升的標(biāo)記 // 否則調(diào)用 getConstantType 獲取子節(jié)點(diǎn)的靜態(tài)類(lèi)型:ConstantTypes 定義了子節(jié)點(diǎn)的靜態(tài)類(lèi)型 const constantType = doNotHoistNode ? ConstantTypes.NOT_CONSTANT : getConstantType(child, context) // 如果獲取到的 constantType 枚舉值大于 NOT_CONSTANT if (constantType > ConstantTypes.NOT_CONSTANT) { // 如果節(jié)點(diǎn)可以被提升 if (constantType >= ConstantTypes.CAN_HOIST) { // 則將子節(jié)點(diǎn)的 codegenNode 屬性的 patchFlag 標(biāo)記為 HOISTED ,即可提升 ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``) // 提升節(jié)點(diǎn),將節(jié)點(diǎn)存儲(chǔ)到 轉(zhuǎn)換上下文context 的 hoist 數(shù)組中 child.codegenNode = context.hoist(child.codegenNode!) // 提升節(jié)點(diǎn)數(shù)量自增1 hoistedCount++ continue } } else { // node may contain dynamic children, but its props may be eligible for // hoisting. // 包含動(dòng)態(tài)綁定的節(jié)點(diǎn)本身不會(huì)被提升,該動(dòng)態(tài)節(jié)點(diǎn)上可能存在純靜態(tài)的屬性,靜態(tài)的屬性可以被提升 const codegenNode = child.codegenNode! if (codegenNode.type === NodeTypes.VNODE_CALL) { // 獲取 patchFlag 補(bǔ)丁標(biāo)志 const flag = getPatchFlag(codegenNode) // 如果不存在 patchFlag 補(bǔ)丁標(biāo)志 或者 patchFlag 是文本類(lèi)型 // 并且該節(jié)點(diǎn)的 props 是可以被提升的 if ( (!flag || flag === PatchFlags.NEED_PATCH || flag === PatchFlags.TEXT) && getGeneratedPropsConstantType(child, context) >= ConstantTypes.CAN_HOIST ) { // 獲取節(jié)點(diǎn)的 props,并在轉(zhuǎn)換上下文對(duì)象中執(zhí)行提升操作, // 將被提升的 props 添加到轉(zhuǎn)換上下文context 的 hoist 數(shù)組中 const props = getNodeProps(child) if (props) { codegenNode.props = context.hoist(props) } } // 將節(jié)點(diǎn)的動(dòng)態(tài) props 添加到轉(zhuǎn)換上下文對(duì)象中 if (codegenNode.dynamicProps) { codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps) } } }
首先通過(guò)外部傳入的 doNotHoistNode 參數(shù)來(lái)獲取子節(jié)點(diǎn)的靜態(tài)類(lèi)型。如果 doNotHoistNode 為 true,則將 constantType 的值設(shè)置為 ConstantType 枚舉值中的 NOT_CONSTANT,即不可被提升。否則通過(guò) getConstantType 函數(shù)獲取子節(jié)點(diǎn)的靜態(tài)類(lèi)型。如下面的代碼所示:
// 如果節(jié)點(diǎn)不能被提升,則將 constantType 賦值為 NOT_CONSTANT 不可被提升的標(biāo)記 // 否則調(diào)用 getConstantType 獲取子節(jié)點(diǎn)的靜態(tài)類(lèi)型:ConstantTypes 定義了子節(jié)點(diǎn)的靜態(tài)類(lèi)型 const constantType = doNotHoistNode ? ConstantTypes.NOT_CONSTANT : getConstantType(child, context)
接下來(lái)通過(guò)判斷 constantType 的枚舉值來(lái)處理是需要提升靜態(tài)節(jié)點(diǎn)還是提升動(dòng)態(tài)節(jié)點(diǎn)的靜態(tài)屬性。
- 如果獲取到的 constantType 枚舉值大于 NOT_CONSTANT,說(shuō)明該節(jié)點(diǎn)可能被提升或序列化為字符串。
- 如果該節(jié)點(diǎn)可以被提升,則將節(jié)點(diǎn) codegenNode 屬性的 patchFlag 標(biāo)記為 PatchFlags.HOISTED ,即可提升。
然后執(zhí)行轉(zhuǎn)換器上下文中的 hoist 方法,將該節(jié)點(diǎn)存儲(chǔ)到轉(zhuǎn)換上下文context 的 hoist 數(shù)組中,該數(shù)組中存儲(chǔ)的是可被提升的節(jié)點(diǎn)。如下面的代碼所示:
// 如果獲取到的 constantType 枚舉值大于 NOT_CONSTANT if (constantType > ConstantTypes.NOT_CONSTANT) { // 如果節(jié)點(diǎn)可以被提升 if (constantType >= ConstantTypes.CAN_HOIST) { // 則將子節(jié)點(diǎn)的 codegenNode 屬性的 patchFlag 標(biāo)記為 HOISTED ,即可提升 ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``) // 提升節(jié)點(diǎn),將節(jié)點(diǎn)存儲(chǔ)到 轉(zhuǎn)換上下文context 的 hoist 數(shù)組中 child.codegenNode = context.hoist(child.codegenNode!) // 提升節(jié)點(diǎn)數(shù)量自增1 hoistedCount++ continue } }
如果獲取到的 constantType 枚舉值不大于 NOT_CONSTANT,說(shuō)明該節(jié)點(diǎn)包含動(dòng)態(tài)綁定,包含動(dòng)態(tài)綁定的節(jié)點(diǎn)上如果存在純靜態(tài)的 props,那么這些靜態(tài)的 props 是可以被提升的。
從下面的代碼中我們可以看到,在提升靜態(tài)的 props 時(shí),同樣是執(zhí)行轉(zhuǎn)換器上下文中的 hoist 方法,將靜態(tài)的props存儲(chǔ)到轉(zhuǎn)換上下文context 的 hoist 數(shù)組中。
如下面的代碼所示:
else { // node may contain dynamic children, but its props may be eligible for // hoisting. // 包含動(dòng)態(tài)綁定的節(jié)點(diǎn)本身不會(huì)被提升,該動(dòng)態(tài)節(jié)點(diǎn)上可能存在純靜態(tài)的屬性,靜態(tài)的屬性可以被提升 const codegenNode = child.codegenNode! if (codegenNode.type === NodeTypes.VNODE_CALL) { // 獲取 patchFlag 補(bǔ)丁標(biāo)志 const flag = getPatchFlag(codegenNode) // 如果不存在 patchFlag 補(bǔ)丁標(biāo)志 或者 patchFlag 是文本類(lèi)型 // 并且該節(jié)點(diǎn)的 props 是可以被提升的 if ( (!flag || flag === PatchFlags.NEED_PATCH || flag === PatchFlags.TEXT) && getGeneratedPropsConstantType(child, context) >= ConstantTypes.CAN_HOIST ) { // 獲取節(jié)點(diǎn)的 props,并在轉(zhuǎn)換上下文對(duì)象中執(zhí)行提升操作, // 將被提升的 props 添加到轉(zhuǎn)換上下文context 的 hoist 數(shù)組中 const props = getNodeProps(child) if (props) { codegenNode.props = context.hoist(props) } } // 將節(jié)點(diǎn)的動(dòng)態(tài) props 添加到轉(zhuǎn)換上下文對(duì)象中 if (codegenNode.dynamicProps) { codegenNode.dynamicProps = context.hoist(codegenNode.dynamicProps) } } }
4、如果節(jié)點(diǎn)類(lèi)型時(shí)是 TEXT_CALL 類(lèi)型并且該節(jié)點(diǎn)可以被提升,則同樣執(zhí)行轉(zhuǎn)換器上下文中的 hoist 方法,將節(jié)點(diǎn)存儲(chǔ)到轉(zhuǎn)換上下文context 的 hoist 數(shù)組中,如下面的代碼所示:
else if ( // 如果是節(jié)點(diǎn)類(lèi)型是 TEXT_CALL,并且節(jié)點(diǎn)可以被提升 child.type === NodeTypes.TEXT_CALL && getConstantType(child.content, context) >= ConstantTypes.CAN_HOIST ) { // 提升節(jié)點(diǎn) child.codegenNode = context.hoist(child.codegenNode) hoistedCount++ }
5、如果子節(jié)點(diǎn)是組件,則遞歸調(diào)用 walk 函數(shù),繼續(xù)遍歷子節(jié)點(diǎn),以判斷插槽中的節(jié)點(diǎn)及節(jié)點(diǎn)屬性是否可以被提升。如下面的代碼所示:
if (child.type === NodeTypes.ELEMENT) { // 如果子節(jié)點(diǎn)的 tagType 是組件,則繼續(xù)遍歷子節(jié)點(diǎn) // 以判斷插槽中的情況 const isComponent = child.tagType === ElementTypes.COMPONENT if (isComponent) { context.scopes.vSlot++ } // 執(zhí)行 walk函數(shù),繼續(xù)判斷插槽中的節(jié)點(diǎn)及節(jié)點(diǎn)屬性是否可以被提升 walk(child, context) if (isComponent) { context.scopes.vSlot-- } }
6、如果節(jié)點(diǎn)上帶有 v-for 指令或 v-if 指令,則遞歸調(diào)用 walk 函數(shù),繼續(xù)判斷子節(jié)點(diǎn)是否可以被提升。如果 v-for 指令的節(jié)點(diǎn)只有一個(gè)子節(jié)點(diǎn),v-if 指令的節(jié)點(diǎn)只有一個(gè)分支條件,則不進(jìn)行提升。如下面的代碼所示:
else if (child.type === NodeTypes.FOR) { // Do not hoist v-for single child because it has to be a block // 帶有 v-for 指令的節(jié)點(diǎn)是一個(gè) Block // 如果 v-for 的節(jié)點(diǎn)中只有一個(gè)子節(jié)點(diǎn),則不能被提升 walk(child, context, child.children.length === 1) } else if (child.type === NodeTypes.IF) { // 帶有 v-if 指令的節(jié)點(diǎn)是一個(gè) Block for (let i = 0; i < child.branches.length; i++) { // Do not hoist v-if single child because it has to be a block // 如果只有一個(gè)分支條件,則不進(jìn)行提升 walk( child.branches[i], context, child.branches[i].children.length === 1 ) } }
walk 函數(shù)流程圖
總結(jié)
靜態(tài)提升,就是指在編譯器編譯的過(guò)程中,將一些靜態(tài)的節(jié)點(diǎn)或?qū)傩蕴嵘戒秩竞瘮?shù)之外。它能夠減少更新時(shí)創(chuàng)建虛擬 DOM 帶來(lái)的性能開(kāi)銷(xiāo)和內(nèi)存占用。
對(duì)于純靜態(tài)的節(jié)點(diǎn)和動(dòng)態(tài)節(jié)點(diǎn)上的純靜態(tài)屬性,則直接執(zhí)行轉(zhuǎn)換器上下文中的 hoist 方法,將節(jié)點(diǎn)或?qū)傩赃M(jìn)行提升。如果節(jié)點(diǎn)是組件、節(jié)點(diǎn)上帶有 v-for 指令或v-if 指令,則遞歸調(diào)用 walk 函數(shù)判斷子節(jié)點(diǎn)是否可以被提升。
以上就是Vue3 源碼解讀靜態(tài)提升詳解的詳細(xì)內(nèi)容,更多關(guān)于Vue3 靜態(tài)提升的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue3.2?setup語(yǔ)法糖及Hook函數(shù)基本使用
這篇文章主要為大家介紹了Vue3.2?setup語(yǔ)法糖及Hook函數(shù)基本使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07vuejs2.0實(shí)現(xiàn)分頁(yè)組件使用$emit進(jìn)行事件監(jiān)聽(tīng)數(shù)據(jù)傳遞的方法
這篇文章主要介紹了vuejs2.0實(shí)現(xiàn)分頁(yè)組件使用$emit進(jìn)行事件監(jiān)聽(tīng)數(shù)據(jù)傳遞的方法,非常不錯(cuò),具有參考借鑒價(jià)值,,需要的朋友可以參考下2017-02-02在Vue項(xiàng)目中取消ESLint代碼檢測(cè)的步驟講解
今天小編就為大家分享一篇關(guān)于在Vue項(xiàng)目中取消ESLint代碼檢測(cè)的步驟講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01Vue.js 無(wú)限滾動(dòng)列表性能優(yōu)化方案
這篇文章主要介紹了Vue.js 無(wú)限滾動(dòng)列表性能優(yōu)化方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12基于vue-ssr的靜態(tài)網(wǎng)站生成器VuePress 初體驗(yàn)
VuePress為每一個(gè)由它生成的頁(yè)面提供預(yù)加載的html,不僅加載速度極佳,同時(shí)對(duì)seo非常友好。這篇文章主要介紹了基于vue-ssr的靜態(tài)網(wǎng)站生成器VuePress 初體驗(yàn),需要的朋友可以參考下2018-04-04Vue利用computed配合watch實(shí)現(xiàn)監(jiān)聽(tīng)多個(gè)屬性的變化
這篇文章主要給大家介紹了在Vue中巧用computed配合watch實(shí)現(xiàn)監(jiān)聽(tīng)多個(gè)屬性的變化的方法,文中有詳細(xì)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2023-10-10詳解vue數(shù)據(jù)渲染出現(xiàn)閃爍問(wèn)題
本篇文章主要介紹了vue數(shù)據(jù)渲染出現(xiàn)閃爍問(wèn)題,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06