]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(portal): portal should always remove its children when unmounted
authorEvan You <yyx990803@gmail.com>
Wed, 25 Mar 2020 21:27:55 +0000 (17:27 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 25 Mar 2020 21:27:55 +0000 (17:27 -0400)
packages/compiler-core/src/transforms/vIf.ts
packages/runtime-core/__tests__/components/Portal.spec.ts
packages/runtime-core/__tests__/components/__snapshots__/Portal.spec.ts.snap
packages/runtime-core/src/components/Portal.ts
packages/runtime-core/src/renderer.ts

index cb0d6bd387565884371e2493d612c2bca848a15c..ac60c4cd7d688de9ed1b95c4438a8fc4994a479d 100644 (file)
@@ -26,7 +26,8 @@ import {
   CREATE_BLOCK,
   FRAGMENT,
   CREATE_COMMENT,
-  OPEN_BLOCK
+  OPEN_BLOCK,
+  PORTAL
 } from '../runtimeHelpers'
 import { injectProp } from '../utils'
 import { PatchFlags, PatchFlagNames } from '@vue/shared'
@@ -216,7 +217,9 @@ function createChildrenCodegenNode(
       vnodeCall.type === NodeTypes.VNODE_CALL &&
       // component vnodes are always tracked and its children are
       // 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
       helper(OPEN_BLOCK)
index bdd50c7fd53773724201f1ea24ca530321aacecc..6c5c36bfda4113b514af7a3d60d1a7fb5141cfbb 100644 (file)
@@ -76,4 +76,21 @@ describe('renderer: portal', () => {
 
     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('')
+  })
 })
index 4a47a585826e0ec2b697a7d4cd031c38a243613a..3d4af4fdffd66d73c95d78e0cc75288ab51c8e6b 100644 (file)
@@ -1,5 +1,7 @@
 // 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 2`] = `""`;
index 46855ef752c7109b8415571a0c62fce03ee51e73..beac7e8ad06765eb33688adb749759b5fc892d6c 100644 (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])
+      }
+    }
   }
 }
 
index e80a4976c13873f63088b6268f2ca44f7f9de9c0..929ea38fd20dd10e60e02a47e5f8e82afa18ae73 100644 (file)
@@ -139,6 +139,7 @@ export interface RendererInternals<
 > {
   p: PatchFn
   um: UnmountFn
+  r: RemoveFn
   m: MoveFn
   mt: MountComponentFn
   mc: MountChildrenFn
@@ -210,6 +211,8 @@ type UnmountFn = (
   doRemove?: boolean
 ) => void
 
+type RemoveFn = (vnode: VNode) => void
+
 type UnmountChildrenFn = (
   children: VNode[],
   parentComponent: ComponentInternalInstance | null,
@@ -1688,6 +1691,11 @@ function baseCreateRenderer(
         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) {
         remove(vnode)
       }
@@ -1702,7 +1710,7 @@ function baseCreateRenderer(
     }
   }
 
-  const remove = (vnode: VNode) => {
+  const remove: RemoveFn = vnode => {
     const { type, el, anchor, transition } = vnode
     if (type === Fragment) {
       removeFragment(el!, anchor!)
@@ -1888,6 +1896,7 @@ function baseCreateRenderer(
     p: patch,
     um: unmount,
     m: move,
+    r: remove,
     mt: mountComponent,
     mc: mountChildren,
     pc: patchChildren,