Skip to content

Commit f02e2f9

Browse files
committed
fix(runtime-core): disable block tracking when calling compiled slot function in tempalte expressions
fix #1745, fix #1918
1 parent c0411b3 commit f02e2f9

File tree

3 files changed

+22
-14
lines changed

3 files changed

+22
-14
lines changed

packages/runtime-core/src/helpers/renderSlot.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import { PatchFlags, SlotFlags } from '@vue/shared'
1111
import { warn } from '../warning'
1212

13-
export let shouldTrackInSlotRendering = 0
13+
export let isRenderingCompiledSlot = 0
1414

1515
/**
1616
* Compiler runtime helper for rendering `<slot/>`
@@ -39,7 +39,7 @@ export function renderSlot(
3939
// invocation interfering with template-based block tracking, but in
4040
// `renderSlot` we can be sure that it's template-based so we can force
4141
// enable it.
42-
shouldTrackInSlotRendering++
42+
isRenderingCompiledSlot++
4343
const rendered = (openBlock(),
4444
createBlock(
4545
Fragment,
@@ -49,6 +49,6 @@ export function renderSlot(
4949
? PatchFlags.STABLE_FRAGMENT
5050
: PatchFlags.BAIL
5151
))
52-
shouldTrackInSlotRendering--
52+
isRenderingCompiledSlot--
5353
return rendered
5454
}

packages/runtime-core/src/helpers/withRenderContext.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {
44
currentRenderingInstance
55
} from '../componentRenderUtils'
66
import { ComponentInternalInstance } from '../component'
7-
import { setBlockTracking } from '../vnode'
7+
import { isRenderingCompiledSlot } from './renderSlot'
8+
import { closeBlock, openBlock } from '../vnode'
89

910
/**
1011
* Wrap a slot function to memoize current rendering instance
@@ -16,15 +17,19 @@ export function withCtx(
1617
) {
1718
if (!ctx) return fn
1819
return function renderFnWithContext() {
19-
// By default, compiled slots disables block tracking since the user may
20-
// call it inside a template expression (#1745). It should only track when
21-
// it's called by a template `<slot>`.
22-
setBlockTracking(-1)
20+
// If a user calls a compiled slot inside a template expression (#1745), it
21+
// can mess up block tracking, so by default we need to push a null block to
22+
// avoid that. This isn't necessary if rendering a compiled `<slot>`.
23+
if (!isRenderingCompiledSlot) {
24+
openBlock(true /* null block that disables tracking */)
25+
}
2326
const owner = currentRenderingInstance
2427
setCurrentRenderingInstance(ctx)
2528
const res = fn.apply(null, arguments as any)
2629
setCurrentRenderingInstance(owner)
27-
setBlockTracking(1)
30+
if (!isRenderingCompiledSlot) {
31+
closeBlock()
32+
}
2833
return res
2934
}
3035
}

packages/runtime-core/src/vnode.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import { currentRenderingInstance } from './componentRenderUtils'
3636
import { RendererNode, RendererElement } from './renderer'
3737
import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
3838
import { hmrDirtyComponents } from './hmr'
39-
import { shouldTrackInSlotRendering } from './helpers/renderSlot'
4039

4140
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
4241
__isFragment: true
@@ -153,7 +152,7 @@ export interface VNode<
153152
// can divide a template into nested blocks, and within each block the node
154153
// structure would be stable. This allows us to skip most children diffing
155154
// and only worry about the dynamic nodes (indicated by patch flags).
156-
const blockStack: (VNode[] | null)[] = []
155+
export const blockStack: (VNode[] | null)[] = []
157156
let currentBlock: VNode[] | null = null
158157

159158
/**
@@ -176,6 +175,11 @@ export function openBlock(disableTracking = false) {
176175
blockStack.push((currentBlock = disableTracking ? null : []))
177176
}
178177

178+
export function closeBlock() {
179+
blockStack.pop()
180+
currentBlock = blockStack[blockStack.length - 1] || null
181+
}
182+
179183
// Whether we should be tracking dynamic child nodes inside a block.
180184
// Only tracks when this value is > 0
181185
// We are not using a simple boolean because this value may need to be
@@ -227,8 +231,7 @@ export function createBlock(
227231
// save current block children on the block vnode
228232
vnode.dynamicChildren = currentBlock || EMPTY_ARR
229233
// close block
230-
blockStack.pop()
231-
currentBlock = blockStack[blockStack.length - 1] || null
234+
closeBlock()
232235
// a block is always going to be patched, so track it as a child of its
233236
// parent block
234237
if (currentBlock) {
@@ -403,7 +406,7 @@ function _createVNode(
403406
normalizeChildren(vnode, children)
404407

405408
if (
406-
(shouldTrack > 0 || shouldTrackInSlotRendering > 0) &&
409+
shouldTrack > 0 &&
407410
// avoid a block node from tracking itself
408411
!isBlockNode &&
409412
// has current parent block

0 commit comments

Comments
 (0)