Skip to content

Commit 16cd8ee

Browse files
committed
fix(portal): portal should always remove its children when unmounted
1 parent cb31eb4 commit 16cd8ee

File tree

5 files changed

+48
-3
lines changed

5 files changed

+48
-3
lines changed

packages/compiler-core/src/transforms/vIf.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import {
2626
CREATE_BLOCK,
2727
FRAGMENT,
2828
CREATE_COMMENT,
29-
OPEN_BLOCK
29+
OPEN_BLOCK,
30+
PORTAL
3031
} from '../runtimeHelpers'
3132
import { injectProp } from '../utils'
3233
import { PatchFlags, PatchFlagNames } from '@vue/shared'
@@ -216,7 +217,9 @@ function createChildrenCodegenNode(
216217
vnodeCall.type === NodeTypes.VNODE_CALL &&
217218
// component vnodes are always tracked and its children are
218219
// compiled into slots so no need to make it a block
219-
(firstChild as ElementNode).tagType !== ElementTypes.COMPONENT
220+
((firstChild as ElementNode).tagType !== ElementTypes.COMPONENT ||
221+
// portal has component type but isn't always tracked
222+
vnodeCall.tag === PORTAL)
220223
) {
221224
vnodeCall.isBlock = true
222225
helper(OPEN_BLOCK)

packages/runtime-core/__tests__/components/Portal.spec.ts

+17
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,21 @@ describe('renderer: portal', () => {
7676

7777
expect(serializeInner(target)).toMatchSnapshot()
7878
})
79+
80+
test('should remove children when unmounted', () => {
81+
const target = nodeOps.createElement('div')
82+
const root = nodeOps.createElement('div')
83+
84+
const Comp = defineComponent(() => () => [
85+
h(Portal, { target }, h('div', 'teleported')),
86+
h('div', 'root')
87+
])
88+
render(h(Comp), root)
89+
expect(serializeInner(target)).toMatchInlineSnapshot(
90+
`"<div>teleported</div>"`
91+
)
92+
93+
render(null, root)
94+
expect(serializeInner(target)).toBe('')
95+
})
7996
})

packages/runtime-core/__tests__/components/__snapshots__/Portal.spec.ts.snap

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`renderer: portal should remove children when unmounted 1`] = `"<div>teleported</div>"`;
4+
35
exports[`renderer: portal should update children 1`] = `"<div>teleported</div>"`;
46

57
exports[`renderer: portal should update children 2`] = `""`;

packages/runtime-core/src/components/Portal.ts

+14
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,20 @@ export const PortalImpl = {
113113
}
114114
}
115115
}
116+
},
117+
118+
remove(
119+
vnode: VNode,
120+
{ r: remove, o: { setElementText } }: RendererInternals
121+
) {
122+
const { target, shapeFlag, children } = vnode
123+
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
124+
setElementText(target!, '')
125+
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
126+
for (let i = 0; i < (children as VNode[]).length; i++) {
127+
remove((children as VNode[])[i])
128+
}
129+
}
116130
}
117131
}
118132

packages/runtime-core/src/renderer.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export interface RendererInternals<
139139
> {
140140
p: PatchFn
141141
um: UnmountFn
142+
r: RemoveFn
142143
m: MoveFn
143144
mt: MountComponentFn
144145
mc: MountChildrenFn
@@ -210,6 +211,8 @@ type UnmountFn = (
210211
doRemove?: boolean
211212
) => void
212213

214+
type RemoveFn = (vnode: VNode) => void
215+
213216
type UnmountChildrenFn = (
214217
children: VNode[],
215218
parentComponent: ComponentInternalInstance | null,
@@ -1688,6 +1691,11 @@ function baseCreateRenderer(
16881691
unmountChildren(children as VNode[], parentComponent, parentSuspense)
16891692
}
16901693

1694+
// an unmounted portal should always remove its children
1695+
if (shapeFlag & ShapeFlags.PORTAL) {
1696+
;(vnode.type as typeof PortalImpl).remove(vnode, internals)
1697+
}
1698+
16911699
if (doRemove) {
16921700
remove(vnode)
16931701
}
@@ -1702,7 +1710,7 @@ function baseCreateRenderer(
17021710
}
17031711
}
17041712

1705-
const remove = (vnode: VNode) => {
1713+
const remove: RemoveFn = vnode => {
17061714
const { type, el, anchor, transition } = vnode
17071715
if (type === Fragment) {
17081716
removeFragment(el!, anchor!)
@@ -1888,6 +1896,7 @@ function baseCreateRenderer(
18881896
p: patch,
18891897
um: unmount,
18901898
m: move,
1899+
r: remove,
18911900
mt: mountComponent,
18921901
mc: mountChildren,
18931902
pc: patchChildren,

0 commit comments

Comments
 (0)