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
|
const { setBlockTracking: _setBlockTracking, createElementVNode: _createElementVNode } = _Vue
|
||||||
|
|
||||||
return _cache[0] || (
|
return _cache[0] || (
|
||||||
_setBlockTracking(-1),
|
_setBlockTracking(-1, true),
|
||||||
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||||
_setBlockTracking(1),
|
_setBlockTracking(1),
|
||||||
_cache[0]
|
_cache[0]
|
||||||
|
@ -28,7 +28,7 @@ return function render(_ctx, _cache) {
|
||||||
|
|
||||||
return (_openBlock(), _createElementBlock("div", null, [
|
return (_openBlock(), _createElementBlock("div", null, [
|
||||||
_cache[0] || (
|
_cache[0] || (
|
||||||
_setBlockTracking(-1),
|
_setBlockTracking(-1, true),
|
||||||
(_cache[0] = _createVNode(_component_Comp, { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
(_cache[0] = _createVNode(_component_Comp, { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||||
_setBlockTracking(1),
|
_setBlockTracking(1),
|
||||||
_cache[0]
|
_cache[0]
|
||||||
|
@ -47,7 +47,7 @@ return function render(_ctx, _cache) {
|
||||||
|
|
||||||
return (_openBlock(), _createElementBlock("div", null, [
|
return (_openBlock(), _createElementBlock("div", null, [
|
||||||
_cache[0] || (
|
_cache[0] || (
|
||||||
_setBlockTracking(-1),
|
_setBlockTracking(-1, true),
|
||||||
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
(_cache[0] = _createElementVNode("div", { id: foo }, null, 8 /* PROPS */, ["id"])).cacheIndex = 0,
|
||||||
_setBlockTracking(1),
|
_setBlockTracking(1),
|
||||||
_cache[0]
|
_cache[0]
|
||||||
|
@ -66,7 +66,7 @@ return function render(_ctx, _cache) {
|
||||||
|
|
||||||
return (_openBlock(), _createElementBlock("div", null, [
|
return (_openBlock(), _createElementBlock("div", null, [
|
||||||
_cache[0] || (
|
_cache[0] || (
|
||||||
_setBlockTracking(-1),
|
_setBlockTracking(-1, true),
|
||||||
(_cache[0] = _renderSlot($slots, "default")).cacheIndex = 0,
|
(_cache[0] = _renderSlot($slots, "default")).cacheIndex = 0,
|
||||||
_setBlockTracking(1),
|
_setBlockTracking(1),
|
||||||
_cache[0]
|
_cache[0]
|
||||||
|
@ -85,7 +85,7 @@ return function render(_ctx, _cache) {
|
||||||
|
|
||||||
return (_openBlock(), _createElementBlock("div", null, [
|
return (_openBlock(), _createElementBlock("div", null, [
|
||||||
_cache[0] || (
|
_cache[0] || (
|
||||||
_setBlockTracking(-1),
|
_setBlockTracking(-1, true),
|
||||||
(_cache[0] = _createElementVNode("div")).cacheIndex = 0,
|
(_cache[0] = _createElementVNode("div")).cacheIndex = 0,
|
||||||
_setBlockTracking(1),
|
_setBlockTracking(1),
|
||||||
_cache[0]
|
_cache[0]
|
||||||
|
|
|
@ -418,6 +418,7 @@ export interface CacheExpression extends Node {
|
||||||
index: number
|
index: number
|
||||||
value: JSChildNode
|
value: JSChildNode
|
||||||
needPauseTracking: boolean
|
needPauseTracking: boolean
|
||||||
|
inVOnce: boolean
|
||||||
needArraySpread: boolean
|
needArraySpread: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,12 +775,14 @@ export function createCacheExpression(
|
||||||
index: number,
|
index: number,
|
||||||
value: JSChildNode,
|
value: JSChildNode,
|
||||||
needPauseTracking: boolean = false,
|
needPauseTracking: boolean = false,
|
||||||
|
inVOnce: boolean = false,
|
||||||
): CacheExpression {
|
): CacheExpression {
|
||||||
return {
|
return {
|
||||||
type: NodeTypes.JS_CACHE_EXPRESSION,
|
type: NodeTypes.JS_CACHE_EXPRESSION,
|
||||||
index,
|
index,
|
||||||
value,
|
value,
|
||||||
needPauseTracking: needPauseTracking,
|
needPauseTracking: needPauseTracking,
|
||||||
|
inVOnce,
|
||||||
needArraySpread: false,
|
needArraySpread: false,
|
||||||
loc: locStub,
|
loc: locStub,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1017,7 +1017,9 @@ function genCacheExpression(node: CacheExpression, context: CodegenContext) {
|
||||||
push(`_cache[${node.index}] || (`)
|
push(`_cache[${node.index}] || (`)
|
||||||
if (needPauseTracking) {
|
if (needPauseTracking) {
|
||||||
indent()
|
indent()
|
||||||
push(`${helper(SET_BLOCK_TRACKING)}(-1),`)
|
push(`${helper(SET_BLOCK_TRACKING)}(-1`)
|
||||||
|
if (node.inVOnce) push(`, true`)
|
||||||
|
push(`),`)
|
||||||
newline()
|
newline()
|
||||||
push(`(`)
|
push(`(`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ export interface TransformContext
|
||||||
addIdentifiers(exp: ExpressionNode | string): void
|
addIdentifiers(exp: ExpressionNode | string): void
|
||||||
removeIdentifiers(exp: ExpressionNode | string): void
|
removeIdentifiers(exp: ExpressionNode | string): void
|
||||||
hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
|
hoist(exp: string | JSChildNode | ArrayExpression): SimpleExpressionNode
|
||||||
cache(exp: JSChildNode, isVNode?: boolean): CacheExpression
|
cache(exp: JSChildNode, isVNode?: boolean, inVOnce?: boolean): CacheExpression
|
||||||
constantCache: WeakMap<TemplateChildNode, ConstantTypes>
|
constantCache: WeakMap<TemplateChildNode, ConstantTypes>
|
||||||
|
|
||||||
// 2.x Compat only
|
// 2.x Compat only
|
||||||
|
@ -297,11 +297,12 @@ export function createTransformContext(
|
||||||
identifier.hoisted = exp
|
identifier.hoisted = exp
|
||||||
return identifier
|
return identifier
|
||||||
},
|
},
|
||||||
cache(exp, isVNode = false) {
|
cache(exp, isVNode = false, inVOnce = false) {
|
||||||
const cacheExp = createCacheExpression(
|
const cacheExp = createCacheExpression(
|
||||||
context.cached.length,
|
context.cached.length,
|
||||||
exp,
|
exp,
|
||||||
isVNode,
|
isVNode,
|
||||||
|
inVOnce,
|
||||||
)
|
)
|
||||||
context.cached.push(cacheExp)
|
context.cached.push(cacheExp)
|
||||||
return cacheExp
|
return cacheExp
|
||||||
|
|
|
@ -17,7 +17,11 @@ export const transformOnce: NodeTransform = (node, context) => {
|
||||||
context.inVOnce = false
|
context.inVOnce = false
|
||||||
const cur = context.currentNode as ElementNode | IfNode | ForNode
|
const cur = context.currentNode as ElementNode | IfNode | ForNode
|
||||||
if (cur.codegenNode) {
|
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,
|
serializeInner as inner,
|
||||||
nextTick,
|
nextTick,
|
||||||
nodeOps,
|
nodeOps,
|
||||||
|
onBeforeMount,
|
||||||
onBeforeUnmount,
|
onBeforeUnmount,
|
||||||
onUnmounted,
|
onUnmounted,
|
||||||
openBlock,
|
openBlock,
|
||||||
|
@ -1199,7 +1200,7 @@ describe('renderer: optimized mode', () => {
|
||||||
createBlock('div', null, [
|
createBlock('div', null, [
|
||||||
createVNode('div', null, [
|
createVNode('div', null, [
|
||||||
cache[0] ||
|
cache[0] ||
|
||||||
(setBlockTracking(-1),
|
(setBlockTracking(-1, true),
|
||||||
((cache[0] = createVNode('div', null, [
|
((cache[0] = createVNode('div', null, [
|
||||||
createVNode(Child),
|
createVNode(Child),
|
||||||
])).cacheIndex = 0),
|
])).cacheIndex = 0),
|
||||||
|
@ -1233,4 +1234,64 @@ describe('renderer: optimized mode', () => {
|
||||||
expect(inner(root)).toBe('<!--v-if-->')
|
expect(inner(root)).toBe('<!--v-if-->')
|
||||||
expect(spyUnmounted).toHaveBeenCalledTimes(2)
|
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 =
|
const vnode =
|
||||||
(openBlock(),
|
(openBlock(),
|
||||||
createBlock('div', null, [
|
createBlock('div', null, [
|
||||||
setBlockTracking(-1),
|
setBlockTracking(-1, true),
|
||||||
(vnode1 = (openBlock(), createBlock('div'))),
|
(vnode1 = (openBlock(), createBlock('div'))),
|
||||||
setBlockTracking(1),
|
setBlockTracking(1),
|
||||||
vnode1,
|
vnode1,
|
||||||
|
|
|
@ -301,7 +301,7 @@ export let isBlockTreeEnabled = 1
|
||||||
*
|
*
|
||||||
* ``` js
|
* ``` js
|
||||||
* _cache[1] || (
|
* _cache[1] || (
|
||||||
* setBlockTracking(-1),
|
* setBlockTracking(-1, true),
|
||||||
* _cache[1] = createVNode(...),
|
* _cache[1] = createVNode(...),
|
||||||
* setBlockTracking(1),
|
* setBlockTracking(1),
|
||||||
* _cache[1]
|
* _cache[1]
|
||||||
|
@ -310,11 +310,11 @@ export let isBlockTreeEnabled = 1
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function setBlockTracking(value: number): void {
|
export function setBlockTracking(value: number, inVOnce = false): void {
|
||||||
isBlockTreeEnabled += value
|
isBlockTreeEnabled += value
|
||||||
if (value < 0 && currentBlock) {
|
if (value < 0 && currentBlock && inVOnce) {
|
||||||
// mark current block so it doesn't take fast path and skip possible
|
// mark current block so it doesn't take fast path and skip possible
|
||||||
// nested components duriung unmount
|
// nested components during unmount
|
||||||
currentBlock.hasOnce = true
|
currentBlock.hasOnce = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue