From 18d2fac8584192935a51ec893930ea1c2210b7bf Mon Sep 17 00:00:00 2001 From: daiwei Date: Wed, 23 Jul 2025 11:13:20 +0800 Subject: [PATCH 1/7] fix(compiler-core): prevent cached array children from retaining detached dom nodes --- .../__tests__/transforms/cacheStatic.spec.ts | 10 --- packages/compiler-core/src/ast.ts | 2 + packages/compiler-core/src/codegen.ts | 5 +- .../src/transforms/cacheStatic.ts | 73 ++++++++----------- .../__tests__/componentSlots.spec.ts | 6 +- packages/runtime-core/src/componentSlots.ts | 11 +-- .../runtime-core/src/helpers/renderSlot.ts | 15 +++- packages/runtime-core/src/renderer.ts | 19 +---- packages/runtime-core/src/vnode.ts | 12 ++- 9 files changed, 61 insertions(+), 92 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts b/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts index 74f6caca328..75358466219 100644 --- a/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts +++ b/packages/compiler-core/__tests__/transforms/cacheStatic.spec.ts @@ -170,11 +170,6 @@ describe('compiler: cacheStatic transform', () => { { /* _ slot flag */ }, - { - type: NodeTypes.JS_PROPERTY, - key: { content: '__' }, - value: { content: '[0]' }, - }, ], }) }) @@ -202,11 +197,6 @@ describe('compiler: cacheStatic transform', () => { { /* _ slot flag */ }, - { - type: NodeTypes.JS_PROPERTY, - key: { content: '__' }, - value: { content: '[0]' }, - }, ], }) }) diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 2d6df9d9010..be2b4798770 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -420,6 +420,7 @@ export interface CacheExpression extends Node { needPauseTracking: boolean inVOnce: boolean needArraySpread: boolean + cachedAsArray: boolean } export interface MemoExpression extends CallExpression { @@ -784,6 +785,7 @@ export function createCacheExpression( needPauseTracking: needPauseTracking, inVOnce, needArraySpread: false, + cachedAsArray: false, loc: locStub, } } diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 6b4559fabb2..5500f2e9ba2 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -1012,7 +1012,7 @@ function genConditionalExpression( function genCacheExpression(node: CacheExpression, context: CodegenContext) { const { push, helper, indent, deindent, newline } = context - const { needPauseTracking, needArraySpread } = node + const { needPauseTracking, needArraySpread, cachedAsArray } = node if (needArraySpread) { push(`[...(`) } @@ -1027,6 +1027,9 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) { } push(`_cache[${node.index}] = `) genNode(node.value, context) + if (cachedAsArray) { + push(`, _cache[${node.index}].patchFlag = -1, _cache[${node.index}]`) + } if (needPauseTracking) { push(`).cacheIndex = ${node.index},`) newline() diff --git a/packages/compiler-core/src/transforms/cacheStatic.ts b/packages/compiler-core/src/transforms/cacheStatic.ts index 0f112e19cad..31c285ba7f7 100644 --- a/packages/compiler-core/src/transforms/cacheStatic.ts +++ b/packages/compiler-core/src/transforms/cacheStatic.ts @@ -12,14 +12,11 @@ import { type RootNode, type SimpleExpressionNode, type SlotFunctionExpression, - type SlotsObjectProperty, type TemplateChildNode, type TemplateNode, type TextCallNode, type VNodeCall, createArrayExpression, - createObjectProperty, - createSimpleExpression, getVNodeBlockHelper, getVNodeHelper, } from '../ast' @@ -70,7 +67,10 @@ function walk( inFor = false, ) { const { children } = node - const toCache: (PlainElementNode | TextCallNode)[] = [] + const toCacheMap = new Map< + PlainElementNode | TextCallNode, + undefined | (() => void) + >() for (let i = 0; i < children.length; i++) { const child = children[i] // only plain elements & text calls are eligible for caching. @@ -83,8 +83,11 @@ function walk( : getConstantType(child, context) if (constantType > ConstantTypes.NOT_CONSTANT) { if (constantType >= ConstantTypes.CAN_CACHE) { - ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.CACHED - toCache.push(child) + toCacheMap.set( + child, + () => + ((child.codegenNode as VNodeCall).patchFlag = PatchFlags.CACHED), + ) continue } } else { @@ -115,16 +118,17 @@ function walk( ? ConstantTypes.NOT_CONSTANT : getConstantType(child, context) if (constantType >= ConstantTypes.CAN_CACHE) { - if ( - child.codegenNode.type === NodeTypes.JS_CALL_EXPRESSION && - child.codegenNode.arguments.length > 0 - ) { - child.codegenNode.arguments.push( - PatchFlags.CACHED + - (__DEV__ ? ` /* ${PatchFlagNames[PatchFlags.CACHED]} */` : ``), - ) - } - toCache.push(child) + toCacheMap.set(child, () => { + if ( + child.codegenNode.type === NodeTypes.JS_CALL_EXPRESSION && + child.codegenNode.arguments.length > 0 + ) { + child.codegenNode.arguments.push( + PatchFlags.CACHED + + (__DEV__ ? ` /* ${PatchFlagNames[PatchFlags.CACHED]} */` : ``), + ) + } + }) continue } } @@ -157,8 +161,7 @@ function walk( } let cachedAsArray = false - const slotCacheKeys = [] - if (toCache.length === children.length && node.type === NodeTypes.ELEMENT) { + if (toCacheMap.size === children.length && node.type === NodeTypes.ELEMENT) { if ( node.tagType === ElementTypes.ELEMENT && node.codegenNode && @@ -181,7 +184,6 @@ function walk( // default slot const slot = getSlotNode(node.codegenNode, 'default') if (slot) { - slotCacheKeys.push(context.cached.length) slot.returns = getCacheExpression( createArrayExpression(slot.returns as TemplateChildNode[]), ) @@ -205,7 +207,6 @@ function walk( slotName.arg && getSlotNode(parent.codegenNode, slotName.arg) if (slot) { - slotCacheKeys.push(context.cached.length) slot.returns = getCacheExpression( createArrayExpression(slot.returns as TemplateChildNode[]), ) @@ -215,33 +216,16 @@ function walk( } if (!cachedAsArray) { - for (const child of toCache) { - slotCacheKeys.push(context.cached.length) + for (const [child, setupCache] of toCacheMap) { + if (setupCache) setupCache() child.codegenNode = context.cache(child.codegenNode!) } } - // put the slot cached keys on the slot object, so that the cache - // can be removed when component unmounting to prevent memory leaks - if ( - slotCacheKeys.length && - node.type === NodeTypes.ELEMENT && - node.tagType === ElementTypes.COMPONENT && - node.codegenNode && - node.codegenNode.type === NodeTypes.VNODE_CALL && - node.codegenNode.children && - !isArray(node.codegenNode.children) && - node.codegenNode.children.type === NodeTypes.JS_OBJECT_EXPRESSION - ) { - node.codegenNode.children.properties.push( - createObjectProperty( - `__`, - createSimpleExpression(JSON.stringify(slotCacheKeys), false), - ) as SlotsObjectProperty, - ) - } - - function getCacheExpression(value: JSChildNode): CacheExpression { + function getCacheExpression( + value: JSChildNode, + cachedAsArray: boolean = true, + ): CacheExpression { const exp = context.cache(value) // #6978, #7138, #7114 // a cached children array inside v-for can caused HMR errors since @@ -249,6 +233,7 @@ function walk( if (inFor && context.hmr) { exp.needArraySpread = true } + exp.cachedAsArray = cachedAsArray return exp } @@ -268,7 +253,7 @@ function walk( } } - if (toCache.length && context.transformHoist) { + if (toCacheMap.size && context.transformHoist) { context.transformHoist(children, context, node) } } diff --git a/packages/runtime-core/__tests__/componentSlots.spec.ts b/packages/runtime-core/__tests__/componentSlots.spec.ts index 765fce33e42..458731dd150 100644 --- a/packages/runtime-core/__tests__/componentSlots.spec.ts +++ b/packages/runtime-core/__tests__/componentSlots.spec.ts @@ -56,14 +56,10 @@ describe('component: slots', () => { expect(Object.getOwnPropertyDescriptor(slots, '_')!.enumerable).toBe( false, ) - expect(slots).toHaveProperty('__') - expect(Object.getOwnPropertyDescriptor(slots, '__')!.enumerable).toBe( - false, - ) return h('div') }, } - const slots = { foo: () => {}, _: 1, __: [1] } + const slots = { foo: () => {}, _: 1 } render(createBlock(Comp, null, slots), nodeOps.createElement('div')) }) diff --git a/packages/runtime-core/src/componentSlots.ts b/packages/runtime-core/src/componentSlots.ts index 380728750f8..c19df7ec80f 100644 --- a/packages/runtime-core/src/componentSlots.ts +++ b/packages/runtime-core/src/componentSlots.ts @@ -79,15 +79,10 @@ export type RawSlots = { * @internal */ _?: SlotFlags - /** - * cache indexes for slot content - * @internal - */ - __?: number[] } const isInternalKey = (key: string) => - key === '_' || key === '__' || key === '_ctx' || key === '$stable' + key === '_' || key === '_ctx' || key === '$stable' const normalizeSlotValue = (value: unknown): VNode[] => isArray(value) @@ -194,10 +189,6 @@ export const initSlots = ( ): void => { const slots = (instance.slots = createInternalObject()) if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) { - const cacheIndexes = (children as RawSlots).__ - // make cache indexes marker non-enumerable - if (cacheIndexes) def(slots, '__', cacheIndexes, true) - const type = (children as RawSlots)._ if (type) { assignSlots(slots, children as Slots, optimized) diff --git a/packages/runtime-core/src/helpers/renderSlot.ts b/packages/runtime-core/src/helpers/renderSlot.ts index 92f7dab36b6..37300a3b9cd 100644 --- a/packages/runtime-core/src/helpers/renderSlot.ts +++ b/packages/runtime-core/src/helpers/renderSlot.ts @@ -9,6 +9,7 @@ import { Fragment, type VNode, type VNodeArrayChildren, + cloneVNode, createBlock, createVNode, isVNode, @@ -71,12 +72,24 @@ export function renderSlot( ;(slot as ContextualRenderFn)._d = false } openBlock() - const validSlotContent = slot && ensureValidVNode(slot(props)) + let validSlotContent = slot && ensureValidVNode(slot(props)) const slotKey = props.key || // slot content array of a dynamic conditional slot may have a branch // key attached in the `createSlots` helper, respect that (validSlotContent && (validSlotContent as any).key) + + // if slot content is an cached array, deep clone it to prevent + // cached vnodes from retaining detached DOM nodes + if ( + validSlotContent && + (validSlotContent as any).patchFlag === PatchFlags.CACHED + ) { + validSlotContent = (validSlotContent as VNode[]).map(child => + cloneVNode(child), + ) + } + const rendered = createBlock( Fragment, { diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index f046e93ad85..4265700364f 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2277,17 +2277,7 @@ function baseCreateRenderer( unregisterHMR(instance) } - const { - bum, - scope, - job, - subTree, - um, - m, - a, - parent, - slots: { __: slotCacheKeys }, - } = instance + const { bum, scope, job, subTree, um, m, a } = instance invalidateMount(m) invalidateMount(a) @@ -2296,13 +2286,6 @@ function baseCreateRenderer( invokeArrayFns(bum) } - // remove slots content from parent renderCache - if (parent && isArray(slotCacheKeys)) { - slotCacheKeys.forEach(v => { - parent.renderCache[v] = undefined - }) - } - if ( __COMPAT__ && isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance) diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index cd1ef948d73..3a5189c38b5 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -471,7 +471,11 @@ function createBaseVNode( ref: props && normalizeRef(props), scopeId: currentScopeId, slotScopeIds: null, - children, + children: + // children is a cached array + isArray(children) && (children as any).patchFlag === PatchFlags.CACHED + ? (children as VNode[]).map(child => cloneVNode(child)) + : children, component: null, suspense: null, ssContent: null, @@ -680,7 +684,9 @@ export function cloneVNode( scopeId: vnode.scopeId, slotScopeIds: vnode.slotScopeIds, children: - __DEV__ && patchFlag === PatchFlags.CACHED && isArray(children) + // if vnode is cached, deep clone it's children to prevent cached children + // from retaining detached DOM nodes + patchFlag === PatchFlags.CACHED && isArray(children) ? (children as VNode[]).map(deepCloneVNode) : children, target: vnode.target, @@ -738,7 +744,7 @@ export function cloneVNode( } /** - * Dev only, for HMR of hoisted vnodes reused in v-for + * for HMR of hoisted vnodes reused in v-for * https://github.com/vitejs/vite/issues/2022 */ function deepCloneVNode(vnode: VNode): VNode { From c577ad737a296ea72c4554972203c5398d5bf787 Mon Sep 17 00:00:00 2001 From: daiwei Date: Thu, 24 Jul 2025 11:28:11 +0800 Subject: [PATCH 2/7] chore: tweaks --- .../__snapshots__/cacheStatic.spec.ts.snap | 48 +++++++++---------- packages/compiler-core/src/codegen.ts | 5 +- .../src/transforms/cacheStatic.ts | 40 +++++++--------- .../stringifyStatic.spec.ts.snap | 48 +++++++++---------- .../templateTransformAssetUrl.spec.ts.snap | 4 +- .../templateTransformSrcset.spec.ts.snap | 4 +- .../runtime-core/src/helpers/renderSlot.ts | 15 +----- packages/runtime-core/src/vnode.ts | 6 +-- 8 files changed, 72 insertions(+), 98 deletions(-) diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap index 91a82db5bba..3ff3fac7f09 100644 --- a/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap +++ b/packages/compiler-core/__tests__/transforms/__snapshots__/cacheStatic.spec.ts.snap @@ -7,9 +7,9 @@ return function render(_ctx, _cache) { with (_ctx) { const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createElementVNode("div", { key: "foo" }, null, -1 /* CACHED */) - ]))) + ]).slice()))) } }" `; @@ -21,7 +21,7 @@ return function render(_ctx, _cache) { with (_ctx) { const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createElementVNode("p", null, [ _createElementVNode("span"), _createElementVNode("span") @@ -30,7 +30,7 @@ return function render(_ctx, _cache) { _createElementVNode("span"), _createElementVNode("span") ], -1 /* CACHED */) - ]))) + ]).slice()))) } }" `; @@ -42,11 +42,11 @@ return function render(_ctx, _cache) { with (_ctx) { const { createCommentVNode: _createCommentVNode, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createElementVNode("div", null, [ _createCommentVNode("comment") ], -1 /* CACHED */) - ]))) + ]).slice()))) } }" `; @@ -58,11 +58,11 @@ return function render(_ctx, _cache) { with (_ctx) { const { createElementVNode: _createElementVNode, createTextVNode: _createTextVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createElementVNode("span", null, null, -1 /* CACHED */), _createTextVNode("foo", -1 /* CACHED */), _createElementVNode("div", null, null, -1 /* CACHED */) - ]))) + ]).slice()))) } }" `; @@ -74,9 +74,9 @@ return function render(_ctx, _cache) { with (_ctx) { const { createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createElementVNode("span", { class: "inline" }, "hello", -1 /* CACHED */) - ]))) + ]).slice()))) } }" `; @@ -147,9 +147,9 @@ return function render(_ctx, _cache) { with (_ctx) { const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createElementVNode("span", null, "foo " + _toDisplayString(1) + " " + _toDisplayString(true), -1 /* CACHED */) - ]))) + ]).slice()))) } }" `; @@ -161,9 +161,9 @@ return function render(_ctx, _cache) { with (_ctx) { const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createElementVNode("span", { foo: 0 }, _toDisplayString(1), -1 /* CACHED */) - ]))) + ]).slice()))) } }" `; @@ -177,9 +177,9 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(1, (i) => { - return (_openBlock(), _createElementBlock("div", null, [...(_cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, [...((_cache[0] || (_cache[0] = [ _createElementVNode("span", { class: "hi" }, null, -1 /* CACHED */) - ]))])) + ]).slice()))])) }), 256 /* UNKEYED_FRAGMENT */)) ])) } @@ -215,9 +215,9 @@ return function render(_ctx, _cache) { const _directive_foo = _resolveDirective("foo") return (_openBlock(), _createElementBlock("div", null, [ - _withDirectives((_openBlock(), _createElementBlock("svg", null, _cache[0] || (_cache[0] = [ + _withDirectives((_openBlock(), _createElementBlock("svg", null, (_cache[0] || (_cache[0] = [ _createElementVNode("path", { d: "M2,3H5.5L12" }, null, -1 /* CACHED */) - ]))), [ + ]).slice()))), [ [_directive_foo] ]) ])) @@ -401,9 +401,9 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [ ok - ? (_openBlock(), _createElementBlock("div", _hoisted_1, _cache[0] || (_cache[0] = [ + ? (_openBlock(), _createElementBlock("div", _hoisted_1, (_cache[0] || (_cache[0] = [ _createElementVNode("span", null, null, -1 /* CACHED */) - ]))) + ]).slice()))) : _createCommentVNode("v-if", true) ])) } @@ -422,7 +422,7 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock(_Fragment, null, [ _createCommentVNode("comment"), - _createElementVNode("div", _hoisted_1, _cache[0] || (_cache[0] = [ + _createElementVNode("div", _hoisted_1, (_cache[0] || (_cache[0] = [ _createElementVNode("div", { id: "b" }, [ _createElementVNode("div", { id: "c" }, [ _createElementVNode("div", { id: "d" }, [ @@ -430,7 +430,7 @@ return function render(_ctx, _cache) { ]) ]) ], -1 /* CACHED */) - ])) + ]).slice())) ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */)) } }" @@ -448,9 +448,9 @@ return function render(_ctx, _cache) { return (_openBlock(), _createElementBlock("div", null, [ (_openBlock(true), _createElementBlock(_Fragment, null, _renderList(list, (i) => { - return (_openBlock(), _createElementBlock("div", _hoisted_1, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", _hoisted_1, (_cache[0] || (_cache[0] = [ _createElementVNode("span", null, null, -1 /* CACHED */) - ]))) + ]).slice()))) }), 256 /* UNKEYED_FRAGMENT */)) ])) } diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index 5500f2e9ba2..ab01a149b6e 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -1016,6 +1016,7 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) { if (needArraySpread) { push(`[...(`) } + if (cachedAsArray) push(`(`) push(`_cache[${node.index}] || (`) if (needPauseTracking) { indent() @@ -1027,9 +1028,7 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) { } push(`_cache[${node.index}] = `) genNode(node.value, context) - if (cachedAsArray) { - push(`, _cache[${node.index}].patchFlag = -1, _cache[${node.index}]`) - } + if (cachedAsArray) push(`).slice()`) if (needPauseTracking) { push(`).cacheIndex = ${node.index},`) newline() diff --git a/packages/compiler-core/src/transforms/cacheStatic.ts b/packages/compiler-core/src/transforms/cacheStatic.ts index 31c285ba7f7..294e9e7d6e1 100644 --- a/packages/compiler-core/src/transforms/cacheStatic.ts +++ b/packages/compiler-core/src/transforms/cacheStatic.ts @@ -67,10 +67,7 @@ function walk( inFor = false, ) { const { children } = node - const toCacheMap = new Map< - PlainElementNode | TextCallNode, - undefined | (() => void) - >() + const toCache: (PlainElementNode | TextCallNode)[] = [] for (let i = 0; i < children.length; i++) { const child = children[i] // only plain elements & text calls are eligible for caching. @@ -83,11 +80,8 @@ function walk( : getConstantType(child, context) if (constantType > ConstantTypes.NOT_CONSTANT) { if (constantType >= ConstantTypes.CAN_CACHE) { - toCacheMap.set( - child, - () => - ((child.codegenNode as VNodeCall).patchFlag = PatchFlags.CACHED), - ) + ;(child.codegenNode as VNodeCall).patchFlag = PatchFlags.CACHED + toCache.push(child) continue } } else { @@ -118,17 +112,16 @@ function walk( ? ConstantTypes.NOT_CONSTANT : getConstantType(child, context) if (constantType >= ConstantTypes.CAN_CACHE) { - toCacheMap.set(child, () => { - if ( - child.codegenNode.type === NodeTypes.JS_CALL_EXPRESSION && - child.codegenNode.arguments.length > 0 - ) { - child.codegenNode.arguments.push( - PatchFlags.CACHED + - (__DEV__ ? ` /* ${PatchFlagNames[PatchFlags.CACHED]} */` : ``), - ) - } - }) + if ( + child.codegenNode.type === NodeTypes.JS_CALL_EXPRESSION && + child.codegenNode.arguments.length > 0 + ) { + child.codegenNode.arguments.push( + PatchFlags.CACHED + + (__DEV__ ? ` /* ${PatchFlagNames[PatchFlags.CACHED]} */` : ``), + ) + } + toCache.push(child) continue } } @@ -161,7 +154,7 @@ function walk( } let cachedAsArray = false - if (toCacheMap.size === children.length && node.type === NodeTypes.ELEMENT) { + if (toCache.length === children.length && node.type === NodeTypes.ELEMENT) { if ( node.tagType === ElementTypes.ELEMENT && node.codegenNode && @@ -216,8 +209,7 @@ function walk( } if (!cachedAsArray) { - for (const [child, setupCache] of toCacheMap) { - if (setupCache) setupCache() + for (const child of toCache) { child.codegenNode = context.cache(child.codegenNode!) } } @@ -253,7 +245,7 @@ function walk( } } - if (toCacheMap.size && context.transformHoist) { + if (toCache.length && context.transformHoist) { context.transformHoist(children, context, node) } } diff --git a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap index 5bc40d3fab5..4ca4c1eccf5 100644 --- a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap +++ b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap @@ -4,11 +4,11 @@ exports[`stringify static html > eligible content (elements > 20) + non-eligible "const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue return function render(_ctx, _cache) { - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createStaticVNode("", 20), _createElementVNode("div", { key: "1" }, "1", -1 /* CACHED */), _createStaticVNode("", 20) - ]))) + ]).slice()))) }" `; @@ -16,9 +16,9 @@ exports[`stringify static html > escape 1`] = ` "const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue return function render(_ctx, _cache) { - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createStaticVNode("
1 + <&1 + <&1 + <&1 + <&1 + <&
", 1) - ]))) + ]).slice()))) }" `; @@ -26,9 +26,9 @@ exports[`stringify static html > serializing constant bindings 1`] = ` "const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue return function render(_ctx, _cache) { - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createStaticVNode("
1 + false1 + false1 + false1 + false1 + false
", 1) - ]))) + ]).slice()))) }" `; @@ -36,9 +36,9 @@ exports[`stringify static html > serializing template string style 1`] = ` "const { toDisplayString: _toDisplayString, normalizeClass: _normalizeClass, createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue return function render(_ctx, _cache) { - return (_openBlock(), _createElementBlock("div", null, _cache[0] || (_cache[0] = [ + return (_openBlock(), _createElementBlock("div", null, (_cache[0] || (_cache[0] = [ _createStaticVNode("
1 + false1 + false1 + false1 + false1 + false
", 1) - ]))) + ]).slice()))) }" `; @@ -46,7 +46,7 @@ exports[`stringify static html > should bail for