diff --git a/packages/compiler-core/src/transforms/hoistStatic.ts b/packages/compiler-core/src/transforms/hoistStatic.ts index 59ba445c..38ea88ae 100644 --- a/packages/compiler-core/src/transforms/hoistStatic.ts +++ b/packages/compiler-core/src/transforms/hoistStatic.ts @@ -43,6 +43,7 @@ function walk( resultCache: Map, doNotHoistNode: boolean = false ) { + let hasHoistedNode = false for (let i = 0; i < children.length; i++) { const child = children[i] // only plain elements & text calls are eligible for hoisting. @@ -55,6 +56,7 @@ function walk( ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.HOISTED + (__DEV__ ? ` /* HOISTED */` : ``) child.codegenNode = context.hoist(child.codegenNode!) + hasHoistedNode = true continue } else { // node may contain dynamic children, but its props may be eligible for @@ -81,6 +83,7 @@ function walk( isStaticNode(child.content, resultCache) ) { child.codegenNode = context.hoist(child.codegenNode) + hasHoistedNode = true } // walk further @@ -98,7 +101,7 @@ function walk( } } - if (context.transformHoist) { + if (hasHoistedNode && context.transformHoist) { context.transformHoist(children, context) } } diff --git a/packages/compiler-dom/src/transforms/stringifyStatic.ts b/packages/compiler-dom/src/transforms/stringifyStatic.ts index a48dabcb..a022d040 100644 --- a/packages/compiler-dom/src/transforms/stringifyStatic.ts +++ b/packages/compiler-dom/src/transforms/stringifyStatic.ts @@ -35,9 +35,29 @@ export const enum StringifyThresholds { type StringiableNode = PlainElementNode | TextCallNode -// Turn eligible hoisted static trees into stringied static nodes, e.g. -// const _hoisted_1 = createStaticVNode(`
bar
`) -// This is only performed in non-in-browser compilations. +/** + * Turn eligible hoisted static trees into stringied static nodes, e.g. + * + * ```js + * const _hoisted_1 = createStaticVNode(`
bar
`) + * ``` + * + * A single static vnode can contain stringified content for **multiple** + * consecutive nodes (element and plain text), called a "chunk". + * `@vue/runtime-dom` will create the content via innerHTML in a hidden + * container element and insert all the nodes in place. The call must also + * provide the number of nodes contained in the chunk so that during hydration + * we can know how many nodes the static vnode should adopt. + * + * The optimization scans a children list that contains hoisted nodes, and + * tries to find the largest chunk of consecutive hoisted nodes before running + * into a non-hoisted node or the end of the list. A chunk is then converted + * into a single static vnode and replaces the hoisted expression of the first + * node in the chunk. Other nodes in the chunk are considered "merged" and + * therefore removed from both the hoist list and the children array. + * + * This optimization is only performed in Node.js. + */ export const stringifyStatic: HoistTransform = (children, context) => { let nc = 0 // current node count let ec = 0 // current element with binding count