const { setBlockTracking: _setBlockTracking, createElementVNode: _createElementVNode } = _Vue
return _cache[0] || (
- _setBlockTracking(-1),
+ _setBlockTracking(-1, true),
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
_setBlockTracking(1),
_cache[0]
return (_openBlock(), _createElementBlock("div", null, [
_cache[0] || (
- _setBlockTracking(-1),
+ _setBlockTracking(-1, true),
(_cache[0] = _createVNode(_component_Comp, { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
_setBlockTracking(1),
_cache[0]
return (_openBlock(), _createElementBlock("div", null, [
_cache[0] || (
- _setBlockTracking(-1),
+ _setBlockTracking(-1, true),
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
_setBlockTracking(1),
_cache[0]
return (_openBlock(), _createElementBlock("div", null, [
_cache[0] || (
- _setBlockTracking(-1),
+ _setBlockTracking(-1, true),
(_cache[0] = _renderSlot($slots, "default")).cacheIndex = 0,
_setBlockTracking(1),
_cache[0]
return (_openBlock(), _createElementBlock("div", null, [
_cache[0] || (
- _setBlockTracking(-1),
+ _setBlockTracking(-1, true),
(_cache[0] = _createElementVNode("div")).cacheIndex = 0,
_setBlockTracking(1),
_cache[0]
index: number
value: JSChildNode
needPauseTracking: boolean
+ inVOnce: boolean
needArraySpread: boolean
}
index: number,
value: JSChildNode,
needPauseTracking: boolean = false,
+ inVOnce: boolean = false,
): CacheExpression {
return {
type: NodeTypes.JS_CACHE_EXPRESSION,
index,
value,
needPauseTracking: needPauseTracking,
+ inVOnce,
needArraySpread: false,
loc: locStub,
}
push(`_cache[${node.index}] || (`)
if (needPauseTracking) {
indent()
- push(`${helper(SET_BLOCK_TRACKING)}(-1),`)
+ push(`${helper(SET_BLOCK_TRACKING)}(-1`)
+ if (node.inVOnce) push(`, true`)
+ push(`),`)
newline()
push(`(`)
}
addIdentifiers(exp: ExpressionNode | string): void
removeIdentifiers(exp: ExpressionNode | string): void
hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
- cache(exp: JSChildNode, isVNode?: boolean): CacheExpression
+ cache(exp: JSChildNode, isVNode?: boolean, inVOnce?: boolean): CacheExpression
constantCache: WeakMap<TemplateChildNode, ConstantTypes>
// 2.x Compat only
identifier.hoisted = exp
return identifier
},
- cache(exp, isVNode = false) {
+ cache(exp, isVNode = false, inVOnce = false) {
const cacheExp = createCacheExpression(
context.cached.length,
exp,
isVNode,
+ inVOnce,
)
context.cached.push(cacheExp)
return cacheExp
context.inVOnce = false
const cur = context.currentNode as ElementNode | IfNode | ForNode
if (cur.codegenNode) {
- cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)
+ cur.codegenNode = context.cache(
+ cur.codegenNode,
+ true /* isVNode */,
+ true /* inVOnce */,
+ )
}
}
}
serializeInner as inner,
nextTick,
nodeOps,
+ onBeforeMount,
onBeforeUnmount,
onUnmounted,
openBlock,
createBlock('div', null, [
createVNode('div', null, [
cache[0] ||
- (setBlockTracking(-1),
+ (setBlockTracking(-1, true),
((cache[0] = createVNode('div', null, [
createVNode(Child),
])).cacheIndex = 0),
expect(inner(root)).toBe('<!--v-if-->')
expect(spyUnmounted).toHaveBeenCalledTimes(2)
})
+
+ // #12371
+ test('unmount children when the user calls a compiled slot', async () => {
+ const beforeMountSpy = vi.fn()
+ const beforeUnmountSpy = vi.fn()
+
+ const Child = {
+ setup() {
+ onBeforeMount(beforeMountSpy)
+ onBeforeUnmount(beforeUnmountSpy)
+ return () => 'child'
+ },
+ }
+
+ const Wrapper = {
+ setup(_: any, { slots }: SetupContext) {
+ return () => (
+ openBlock(),
+ createElementBlock('section', null, [
+ (openBlock(),
+ createElementBlock('div', { key: 1 }, [
+ createTextVNode(slots.header!() ? 'foo' : 'bar', 1 /* TEXT */),
+ renderSlot(slots, 'content'),
+ ])),
+ ])
+ )
+ },
+ }
+
+ const show = ref(false)
+ const app = createApp({
+ render() {
+ return show.value
+ ? (openBlock(),
+ createBlock(Wrapper, null, {
+ header: withCtx(() => [createVNode({})]),
+ content: withCtx(() => [createVNode(Child)]),
+ _: 1,
+ }))
+ : createCommentVNode('v-if', true)
+ },
+ })
+
+ app.mount(root)
+ expect(inner(root)).toMatchInlineSnapshot(`"<!--v-if-->"`)
+ expect(beforeMountSpy).toHaveBeenCalledTimes(0)
+ expect(beforeUnmountSpy).toHaveBeenCalledTimes(0)
+
+ show.value = true
+ await nextTick()
+ expect(inner(root)).toMatchInlineSnapshot(
+ `"<section><div>foochild</div></section>"`,
+ )
+ expect(beforeMountSpy).toHaveBeenCalledTimes(1)
+
+ show.value = false
+ await nextTick()
+ expect(inner(root)).toBe('<!--v-if-->')
+ expect(beforeUnmountSpy).toHaveBeenCalledTimes(1)
+ })
})
const vnode =
(openBlock(),
createBlock('div', null, [
- setBlockTracking(-1),
+ setBlockTracking(-1, true),
(vnode1 = (openBlock(), createBlock('div'))),
setBlockTracking(1),
vnode1,
*
* ``` js
* _cache[1] || (
- * setBlockTracking(-1),
+ * setBlockTracking(-1, true),
* _cache[1] = createVNode(...),
* setBlockTracking(1),
* _cache[1]
*
* @private
*/
-export function setBlockTracking(value: number): void {
+export function setBlockTracking(value: number, inVOnce = false): void {
isBlockTreeEnabled += value
- if (value < 0 && currentBlock) {
+ if (value < 0 && currentBlock && inVOnce) {
// mark current block so it doesn't take fast path and skip possible
- // nested components duriung unmount
+ // nested components during unmount
currentBlock.hasOnce = true
}
}