From ff3d7e5e92704a4bfa146268f89acf2748f31b5f Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Thu, 4 Feb 2021 11:24:05 +0800 Subject: [PATCH 1/2] fix(Teleport): component with multi roots should be removed when unmounted --- .../__tests__/components/Teleport.spec.ts | 20 +++++++++++++ .../runtime-core/src/components/Teleport.ts | 30 +++++++++---------- packages/runtime-core/src/renderer.ts | 9 +++++- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/packages/runtime-core/__tests__/components/Teleport.spec.ts b/packages/runtime-core/__tests__/components/Teleport.spec.ts index 2307932ed6b..f6babd9263b 100644 --- a/packages/runtime-core/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-core/__tests__/components/Teleport.spec.ts @@ -147,6 +147,26 @@ describe('renderer: teleport', () => { testUnmount({ to: null, disabled: true }) }) + test('component with multi roots should be removed when unmounted', () => { + const target = nodeOps.createElement('div') + const root = nodeOps.createElement('div') + + const Comp = { + render() { + return [h('p'), h('p')] + } + } + + render( + h(() => [h(Teleport, { to: target }, h(Comp)), h('div', 'root')]), + root + ) + expect(serializeInner(target)).toMatchInlineSnapshot(`"

"`) + + render(null, root) + expect(serializeInner(target)).toBe('') + }) + test('multiple teleport with same target', () => { const target = nodeOps.createElement('div') const root = nodeOps.createElement('div') diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index 7bbad77fbf0..6255d1475c0 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -218,22 +218,22 @@ export const TeleportImpl = { remove( vnode: VNode, - { r: remove, o: { remove: hostRemove } }: RendererInternals, - doRemove: Boolean + parentComponent: ComponentInternalInstance | null, + parentSuspense: SuspenseBoundary | null, + optimized: boolean, + { um: unmount, o: { remove: hostRemove } }: RendererInternals ) { - const { shapeFlag, children, anchor, targetAnchor, target, props } = vnode - - if (target) { - hostRemove(targetAnchor!) - } - - // an unmounted teleport should always remove its children if not disabled - if (doRemove || !isTeleportDisabled(props)) { - hostRemove(anchor!) - if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { - for (let i = 0; i < (children as VNode[]).length; i++) { - remove((children as VNode[])[i]) - } + const { shapeFlag, children, anchor } = vnode + hostRemove(anchor!) + if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { + for (let i = 0; i < (children as VNode[]).length; i++) { + unmount( + (children as VNode[])[i], + parentComponent, + parentSuspense, + true, + optimized + ) } } }, diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index e12d0242fc8..80a6050f793 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2107,8 +2107,15 @@ function baseCreateRenderer( unmountChildren(children as VNode[], parentComponent, parentSuspense) } + // an unmounted teleport should always remove its children if not disabled if (shapeFlag & ShapeFlags.TELEPORT) { - ;(vnode.type as typeof TeleportImpl).remove(vnode, internals, doRemove) + ;(vnode.type as typeof TeleportImpl).remove( + vnode, + parentComponent, + parentSuspense, + optimized, + internals + ) } if (doRemove) { From 503df2d4ad3fc29ab2cd5ab2ed4c85e4aab66997 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 25 Mar 2021 10:59:45 -0400 Subject: [PATCH 2/2] chore: resolve conflict --- .../runtime-core/src/components/Teleport.ts | 35 ++++++++++++------- packages/runtime-core/src/renderer.ts | 4 +-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/packages/runtime-core/src/components/Teleport.ts b/packages/runtime-core/src/components/Teleport.ts index 6255d1475c0..1b51a03ac94 100644 --- a/packages/runtime-core/src/components/Teleport.ts +++ b/packages/runtime-core/src/components/Teleport.ts @@ -21,7 +21,7 @@ export interface TeleportProps { export const isTeleport = (type: any): boolean => type.__isTeleport -export const isTeleportDisabled = (props: VNode['props']): boolean => +const isTeleportDisabled = (props: VNode['props']): boolean => props && (props.disabled || props.disabled === '') const isTargetSVG = (target: RendererElement): boolean => @@ -221,19 +221,28 @@ export const TeleportImpl = { parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, optimized: boolean, - { um: unmount, o: { remove: hostRemove } }: RendererInternals + { um: unmount, o: { remove: hostRemove } }: RendererInternals, + doRemove: Boolean ) { - const { shapeFlag, children, anchor } = vnode - hostRemove(anchor!) - if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { - for (let i = 0; i < (children as VNode[]).length; i++) { - unmount( - (children as VNode[])[i], - parentComponent, - parentSuspense, - true, - optimized - ) + const { shapeFlag, children, anchor, targetAnchor, target, props } = vnode + + if (target) { + hostRemove(targetAnchor!) + } + + // an unmounted teleport should always remove its children if not disabled + if (doRemove || !isTeleportDisabled(props)) { + hostRemove(anchor!) + if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { + for (let i = 0; i < (children as VNode[]).length; i++) { + unmount( + (children as VNode[])[i], + parentComponent, + parentSuspense, + true, + optimized + ) + } } } }, diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 80a6050f793..d1fb8db5468 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2107,14 +2107,14 @@ function baseCreateRenderer( unmountChildren(children as VNode[], parentComponent, parentSuspense) } - // an unmounted teleport should always remove its children if not disabled if (shapeFlag & ShapeFlags.TELEPORT) { ;(vnode.type as typeof TeleportImpl).remove( vnode, parentComponent, parentSuspense, optimized, - internals + internals, + doRemove ) }