Skip to content

Commit 37300fc

Browse files
authoredNov 14, 2024··
fix(v-once): setting hasOnce to current block only when in v-once (#12374)
close #12371
1 parent bee2f5e commit 37300fc

File tree

8 files changed

+86
-15
lines changed

8 files changed

+86
-15
lines changed
 

‎packages/compiler-core/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ return function render(_ctx, _cache) {
88
const { setBlockTracking: _setBlockTracking, createElementVNode: _createElementVNode } = _Vue
99
1010
return _cache[0] || (
11-
_setBlockTracking(-1),
11+
_setBlockTracking(-1, true),
1212
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
1313
_setBlockTracking(1),
1414
_cache[0]
@@ -28,7 +28,7 @@ return function render(_ctx, _cache) {
2828
2929
return (_openBlock(), _createElementBlock("div", null, [
3030
_cache[0] || (
31-
_setBlockTracking(-1),
31+
_setBlockTracking(-1, true),
3232
(_cache[0] = _createVNode(_component_Comp, { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
3333
_setBlockTracking(1),
3434
_cache[0]
@@ -47,7 +47,7 @@ return function render(_ctx, _cache) {
4747
4848
return (_openBlock(), _createElementBlock("div", null, [
4949
_cache[0] || (
50-
_setBlockTracking(-1),
50+
_setBlockTracking(-1, true),
5151
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
5252
_setBlockTracking(1),
5353
_cache[0]
@@ -66,7 +66,7 @@ return function render(_ctx, _cache) {
6666
6767
return (_openBlock(), _createElementBlock("div", null, [
6868
_cache[0] || (
69-
_setBlockTracking(-1),
69+
_setBlockTracking(-1, true),
7070
(_cache[0] = _renderSlot($slots, "default")).cacheIndex = 0,
7171
_setBlockTracking(1),
7272
_cache[0]
@@ -85,7 +85,7 @@ return function render(_ctx, _cache) {
8585
8686
return (_openBlock(), _createElementBlock("div", null, [
8787
_cache[0] || (
88-
_setBlockTracking(-1),
88+
_setBlockTracking(-1, true),
8989
(_cache[0] = _createElementVNode("div")).cacheIndex = 0,
9090
_setBlockTracking(1),
9191
_cache[0]

‎packages/compiler-core/src/ast.ts

+3
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ export interface CacheExpression extends Node {
418418
index: number
419419
value: JSChildNode
420420
needPauseTracking: boolean
421+
inVOnce: boolean
421422
needArraySpread: boolean
422423
}
423424

@@ -774,12 +775,14 @@ export function createCacheExpression(
774775
index: number,
775776
value: JSChildNode,
776777
needPauseTracking: boolean = false,
778+
inVOnce: boolean = false,
777779
): CacheExpression {
778780
return {
779781
type: NodeTypes.JS_CACHE_EXPRESSION,
780782
index,
781783
value,
782784
needPauseTracking: needPauseTracking,
785+
inVOnce,
783786
needArraySpread: false,
784787
loc: locStub,
785788
}

‎packages/compiler-core/src/codegen.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,9 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) {
10171017
push(`_cache[${node.index}] || (`)
10181018
if (needPauseTracking) {
10191019
indent()
1020-
push(`${helper(SET_BLOCK_TRACKING)}(-1),`)
1020+
push(`${helper(SET_BLOCK_TRACKING)}(-1`)
1021+
if (node.inVOnce) push(`, true`)
1022+
push(`),`)
10211023
newline()
10221024
push(`(`)
10231025
}

‎packages/compiler-core/src/transform.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export interface TransformContext
116116
addIdentifiers(exp: ExpressionNode | string): void
117117
removeIdentifiers(exp: ExpressionNode | string): void
118118
hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
119-
cache(exp: JSChildNode, isVNode?: boolean): CacheExpression
119+
cache(exp: JSChildNode, isVNode?: boolean, inVOnce?: boolean): CacheExpression
120120
constantCache: WeakMap<TemplateChildNode, ConstantTypes>
121121

122122
// 2.x Compat only
@@ -297,11 +297,12 @@ export function createTransformContext(
297297
identifier.hoisted = exp
298298
return identifier
299299
},
300-
cache(exp, isVNode = false) {
300+
cache(exp, isVNode = false, inVOnce = false) {
301301
const cacheExp = createCacheExpression(
302302
context.cached.length,
303303
exp,
304304
isVNode,
305+
inVOnce,
305306
)
306307
context.cached.push(cacheExp)
307308
return cacheExp

‎packages/compiler-core/src/transforms/vOnce.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ export const transformOnce: NodeTransform = (node, context) => {
1717
context.inVOnce = false
1818
const cur = context.currentNode as ElementNode | IfNode | ForNode
1919
if (cur.codegenNode) {
20-
cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)
20+
cur.codegenNode = context.cache(
21+
cur.codegenNode,
22+
true /* isVNode */,
23+
true /* inVOnce */,
24+
)
2125
}
2226
}
2327
}

‎packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts

+62-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
serializeInner as inner,
1818
nextTick,
1919
nodeOps,
20+
onBeforeMount,
2021
onBeforeUnmount,
2122
onUnmounted,
2223
openBlock,
@@ -1199,7 +1200,7 @@ describe('renderer: optimized mode', () => {
11991200
createBlock('div', null, [
12001201
createVNode('div', null, [
12011202
cache[0] ||
1202-
(setBlockTracking(-1),
1203+
(setBlockTracking(-1, true),
12031204
((cache[0] = createVNode('div', null, [
12041205
createVNode(Child),
12051206
])).cacheIndex = 0),
@@ -1233,4 +1234,64 @@ describe('renderer: optimized mode', () => {
12331234
expect(inner(root)).toBe('<!--v-if-->')
12341235
expect(spyUnmounted).toHaveBeenCalledTimes(2)
12351236
})
1237+
1238+
// #12371
1239+
test('unmount children when the user calls a compiled slot', async () => {
1240+
const beforeMountSpy = vi.fn()
1241+
const beforeUnmountSpy = vi.fn()
1242+
1243+
const Child = {
1244+
setup() {
1245+
onBeforeMount(beforeMountSpy)
1246+
onBeforeUnmount(beforeUnmountSpy)
1247+
return () => 'child'
1248+
},
1249+
}
1250+
1251+
const Wrapper = {
1252+
setup(_: any, { slots }: SetupContext) {
1253+
return () => (
1254+
openBlock(),
1255+
createElementBlock('section', null, [
1256+
(openBlock(),
1257+
createElementBlock('div', { key: 1 }, [
1258+
createTextVNode(slots.header!() ? 'foo' : 'bar', 1 /* TEXT */),
1259+
renderSlot(slots, 'content'),
1260+
])),
1261+
])
1262+
)
1263+
},
1264+
}
1265+
1266+
const show = ref(false)
1267+
const app = createApp({
1268+
render() {
1269+
return show.value
1270+
? (openBlock(),
1271+
createBlock(Wrapper, null, {
1272+
header: withCtx(() => [createVNode({})]),
1273+
content: withCtx(() => [createVNode(Child)]),
1274+
_: 1,
1275+
}))
1276+
: createCommentVNode('v-if', true)
1277+
},
1278+
})
1279+
1280+
app.mount(root)
1281+
expect(inner(root)).toMatchInlineSnapshot(`"<!--v-if-->"`)
1282+
expect(beforeMountSpy).toHaveBeenCalledTimes(0)
1283+
expect(beforeUnmountSpy).toHaveBeenCalledTimes(0)
1284+
1285+
show.value = true
1286+
await nextTick()
1287+
expect(inner(root)).toMatchInlineSnapshot(
1288+
`"<section><div>foochild</div></section>"`,
1289+
)
1290+
expect(beforeMountSpy).toHaveBeenCalledTimes(1)
1291+
1292+
show.value = false
1293+
await nextTick()
1294+
expect(inner(root)).toBe('<!--v-if-->')
1295+
expect(beforeUnmountSpy).toHaveBeenCalledTimes(1)
1296+
})
12361297
})

‎packages/runtime-core/__tests__/vnode.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ describe('vnode', () => {
629629
const vnode =
630630
(openBlock(),
631631
createBlock('div', null, [
632-
setBlockTracking(-1),
632+
setBlockTracking(-1, true),
633633
(vnode1 = (openBlock(), createBlock('div'))),
634634
setBlockTracking(1),
635635
vnode1,

‎packages/runtime-core/src/vnode.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ export let isBlockTreeEnabled = 1
301301
*
302302
* ``` js
303303
* _cache[1] || (
304-
* setBlockTracking(-1),
304+
* setBlockTracking(-1, true),
305305
* _cache[1] = createVNode(...),
306306
* setBlockTracking(1),
307307
* _cache[1]
@@ -310,11 +310,11 @@ export let isBlockTreeEnabled = 1
310310
*
311311
* @private
312312
*/
313-
export function setBlockTracking(value: number): void {
313+
export function setBlockTracking(value: number, inVOnce = false): void {
314314
isBlockTreeEnabled += value
315-
if (value < 0 && currentBlock) {
315+
if (value < 0 && currentBlock && inVOnce) {
316316
// mark current block so it doesn't take fast path and skip possible
317-
// nested components duriung unmount
317+
// nested components during unmount
318318
currentBlock.hasOnce = true
319319
}
320320
}

0 commit comments

Comments
 (0)
Please sign in to comment.