fix(portal): portal should always remove its children when unmounted

This commit is contained in:
Evan You 2020-03-25 17:27:55 -04:00
parent cb31eb4d0a
commit 16cd8eee78
5 changed files with 48 additions and 3 deletions

View File

@ -26,7 +26,8 @@ import {
CREATE_BLOCK, CREATE_BLOCK,
FRAGMENT, FRAGMENT,
CREATE_COMMENT, CREATE_COMMENT,
OPEN_BLOCK OPEN_BLOCK,
PORTAL
} from '../runtimeHelpers' } from '../runtimeHelpers'
import { injectProp } from '../utils' import { injectProp } from '../utils'
import { PatchFlags, PatchFlagNames } from '@vue/shared' import { PatchFlags, PatchFlagNames } from '@vue/shared'
@ -216,7 +217,9 @@ function createChildrenCodegenNode(
vnodeCall.type === NodeTypes.VNODE_CALL && vnodeCall.type === NodeTypes.VNODE_CALL &&
// component vnodes are always tracked and its children are // component vnodes are always tracked and its children are
// compiled into slots so no need to make it a block // compiled into slots so no need to make it a block
(firstChild as ElementNode).tagType !== ElementTypes.COMPONENT ((firstChild as ElementNode).tagType !== ElementTypes.COMPONENT ||
// portal has component type but isn't always tracked
vnodeCall.tag === PORTAL)
) { ) {
vnodeCall.isBlock = true vnodeCall.isBlock = true
helper(OPEN_BLOCK) helper(OPEN_BLOCK)

View File

@ -76,4 +76,21 @@ describe('renderer: portal', () => {
expect(serializeInner(target)).toMatchSnapshot() expect(serializeInner(target)).toMatchSnapshot()
}) })
test('should remove children when unmounted', () => {
const target = nodeOps.createElement('div')
const root = nodeOps.createElement('div')
const Comp = defineComponent(() => () => [
h(Portal, { target }, h('div', 'teleported')),
h('div', 'root')
])
render(h(Comp), root)
expect(serializeInner(target)).toMatchInlineSnapshot(
`"<div>teleported</div>"`
)
render(null, root)
expect(serializeInner(target)).toBe('')
})
}) })

View File

@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renderer: portal should remove children when unmounted 1`] = `"<div>teleported</div>"`;
exports[`renderer: portal should update children 1`] = `"<div>teleported</div>"`; exports[`renderer: portal should update children 1`] = `"<div>teleported</div>"`;
exports[`renderer: portal should update children 2`] = `""`; exports[`renderer: portal should update children 2`] = `""`;

View File

@ -113,6 +113,20 @@ export const PortalImpl = {
} }
} }
} }
},
remove(
vnode: VNode,
{ r: remove, o: { setElementText } }: RendererInternals
) {
const { target, shapeFlag, children } = vnode
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
setElementText(target!, '')
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as VNode[]).length; i++) {
remove((children as VNode[])[i])
}
}
} }
} }

View File

@ -139,6 +139,7 @@ export interface RendererInternals<
> { > {
p: PatchFn p: PatchFn
um: UnmountFn um: UnmountFn
r: RemoveFn
m: MoveFn m: MoveFn
mt: MountComponentFn mt: MountComponentFn
mc: MountChildrenFn mc: MountChildrenFn
@ -210,6 +211,8 @@ type UnmountFn = (
doRemove?: boolean doRemove?: boolean
) => void ) => void
type RemoveFn = (vnode: VNode) => void
type UnmountChildrenFn = ( type UnmountChildrenFn = (
children: VNode[], children: VNode[],
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
@ -1688,6 +1691,11 @@ function baseCreateRenderer(
unmountChildren(children as VNode[], parentComponent, parentSuspense) unmountChildren(children as VNode[], parentComponent, parentSuspense)
} }
// an unmounted portal should always remove its children
if (shapeFlag & ShapeFlags.PORTAL) {
;(vnode.type as typeof PortalImpl).remove(vnode, internals)
}
if (doRemove) { if (doRemove) {
remove(vnode) remove(vnode)
} }
@ -1702,7 +1710,7 @@ function baseCreateRenderer(
} }
} }
const remove = (vnode: VNode) => { const remove: RemoveFn = vnode => {
const { type, el, anchor, transition } = vnode const { type, el, anchor, transition } = vnode
if (type === Fragment) { if (type === Fragment) {
removeFragment(el!, anchor!) removeFragment(el!, anchor!)
@ -1888,6 +1896,7 @@ function baseCreateRenderer(
p: patch, p: patch,
um: unmount, um: unmount,
m: move, m: move,
r: remove,
mt: mountComponent, mt: mountComponent,
mc: mountChildren, mc: mountChildren,
pc: patchChildren, pc: patchChildren,