mirror of https://github.com/vuejs/core.git
fix(v-once): setting hasOnce to current block only when in v-once (#12374)
close #12371
This commit is contained in:
parent
bee2f5ee62
commit
37300fc261
|
@ -8,7 +8,7 @@ return function render(_ctx, _cache) {
|
|||
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]
|
||||
|
@ -28,7 +28,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
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]
|
||||
|
@ -47,7 +47,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
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]
|
||||
|
@ -66,7 +66,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _renderSlot($slots, "default")).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
@ -85,7 +85,7 @@ return function render(_ctx, _cache) {
|
|||
|
||||
return (_openBlock(), _createElementBlock("div", null, [
|
||||
_cache[0] || (
|
||||
_setBlockTracking(-1),
|
||||
_setBlockTracking(-1, true),
|
||||
(_cache[0] = _createElementVNode("div")).cacheIndex = 0,
|
||||
_setBlockTracking(1),
|
||||
_cache[0]
|
||||
|
|
|
@ -418,6 +418,7 @@ export interface CacheExpression extends Node {
|
|||
index: number
|
||||
value: JSChildNode
|
||||
needPauseTracking: boolean
|
||||
inVOnce: boolean
|
||||
needArraySpread: boolean
|
||||
}
|
||||
|
||||
|
@ -774,12 +775,14 @@ export function createCacheExpression(
|
|||
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,
|
||||
}
|
||||
|
|
|
@ -1017,7 +1017,9 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) {
|
|||
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(`(`)
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ export interface TransformContext
|
|||
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
|
||||
|
@ -297,11 +297,12 @@ export function createTransformContext(
|
|||
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
|
||||
|
|
|
@ -17,7 +17,11 @@ export const transformOnce: NodeTransform = (node, context) => {
|
|||
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 */,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
serializeInner as inner,
|
||||
nextTick,
|
||||
nodeOps,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onUnmounted,
|
||||
openBlock,
|
||||
|
@ -1199,7 +1200,7 @@ describe('renderer: optimized mode', () => {
|
|||
createBlock('div', null, [
|
||||
createVNode('div', null, [
|
||||
cache[0] ||
|
||||
(setBlockTracking(-1),
|
||||
(setBlockTracking(-1, true),
|
||||
((cache[0] = createVNode('div', null, [
|
||||
createVNode(Child),
|
||||
])).cacheIndex = 0),
|
||||
|
@ -1233,4 +1234,64 @@ describe('renderer: optimized mode', () => {
|
|||
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)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -629,7 +629,7 @@ describe('vnode', () => {
|
|||
const vnode =
|
||||
(openBlock(),
|
||||
createBlock('div', null, [
|
||||
setBlockTracking(-1),
|
||||
setBlockTracking(-1, true),
|
||||
(vnode1 = (openBlock(), createBlock('div'))),
|
||||
setBlockTracking(1),
|
||||
vnode1,
|
||||
|
|
|
@ -301,7 +301,7 @@ export let isBlockTreeEnabled = 1
|
|||
*
|
||||
* ``` js
|
||||
* _cache[1] || (
|
||||
* setBlockTracking(-1),
|
||||
* setBlockTracking(-1, true),
|
||||
* _cache[1] = createVNode(...),
|
||||
* setBlockTracking(1),
|
||||
* _cache[1]
|
||||
|
@ -310,11 +310,11 @@ export let isBlockTreeEnabled = 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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue