]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(fragment): properly remove compiler generated fragments
authorEvan You <yyx990803@gmail.com>
Fri, 13 Dec 2019 15:31:40 +0000 (10:31 -0500)
committerEvan You <yyx990803@gmail.com>
Fri, 13 Dec 2019 15:31:40 +0000 (10:31 -0500)
packages/runtime-core/__tests__/rendererFragment.spec.ts
packages/runtime-core/src/renderer.ts

index 7c4be2b3f5b7474a4495e283100ab50c3473a6b2..dd3a27b727229d150859c39190eb015444968c9d 100644 (file)
@@ -260,5 +260,9 @@ describe('renderer: fragment', () => {
       { type: NodeOpTypes.INSERT, targetNode: { type: 'element' } },
       { type: NodeOpTypes.INSERT, targetNode: { type: 'comment' } }
     ])
+
+    // should properly remove nested fragments
+    render(null, root)
+    expect(serializeInner(root)).toBe(``)
   })
 })
index f4f81d54d5da1b9b9753c22ac5d8372ca86fb051..ba981f4f92f8ca9c76d9a061b029114b1d2deb7e 100644 (file)
@@ -1483,17 +1483,7 @@ export function createRenderer<
     parentSuspense: HostSuspenseBoundary | null,
     doRemove?: boolean
   ) {
-    const {
-      el,
-      props,
-      ref,
-      type,
-      children,
-      dynamicChildren,
-      shapeFlag,
-      anchor,
-      transition
-    } = vnode
+    const { props, ref, children, dynamicChildren, shapeFlag } = vnode
 
     // unset ref
     if (ref !== null && parentComponent !== null) {
@@ -1518,56 +1508,62 @@ export function createRenderer<
       invokeDirectiveHook(props.onVnodeBeforeUnmount, parentComponent, vnode)
     }
 
-    const shouldRemoveChildren = type === Fragment && doRemove
     if (dynamicChildren != null) {
-      unmountChildren(
-        dynamicChildren,
-        parentComponent,
-        parentSuspense,
-        shouldRemoveChildren
-      )
+      // fast path for block nodes: only need to unmount dynamic children.
+      unmountChildren(dynamicChildren, parentComponent, parentSuspense)
     } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
-      unmountChildren(
-        children as HostVNode[],
-        parentComponent,
-        parentSuspense,
-        shouldRemoveChildren
-      )
+      unmountChildren(children as HostVNode[], parentComponent, parentSuspense)
     }
 
     if (doRemove) {
-      const remove = () => {
-        hostRemove(vnode.el!)
-        if (anchor != null) hostRemove(anchor)
-        if (
-          transition != null &&
-          !transition.persisted &&
-          transition.afterLeave
-        ) {
-          transition.afterLeave()
-        }
-      }
+      remove(vnode)
+    }
+
+    if (props != null && props.onVnodeUnmounted != null) {
+      queuePostRenderEffect(() => {
+        invokeDirectiveHook(props.onVnodeUnmounted!, parentComponent, vnode)
+      }, parentSuspense)
+    }
+  }
+
+  function remove(vnode: HostVNode) {
+    const { type, el, anchor, children, transition } = vnode
+    const performRemove = () => {
+      hostRemove(el!)
+      if (anchor != null) hostRemove(anchor)
       if (
-        vnode.shapeFlag & ShapeFlags.ELEMENT &&
         transition != null &&
-        !transition.persisted
+        !transition.persisted &&
+        transition.afterLeave
       ) {
-        const { leave, delayLeave } = transition
-        const performLeave = () => leave(el!, remove)
-        if (delayLeave) {
-          delayLeave(vnode.el!, remove, performLeave)
-        } else {
-          performLeave()
-        }
+        transition.afterLeave()
+      }
+    }
+    if (type === Fragment) {
+      performRemove()
+      removeChildren(children as HostVNode[])
+      return
+    }
+    if (
+      vnode.shapeFlag & ShapeFlags.ELEMENT &&
+      transition != null &&
+      !transition.persisted
+    ) {
+      const { leave, delayLeave } = transition
+      const performLeave = () => leave(el!, performRemove)
+      if (delayLeave) {
+        delayLeave(vnode.el!, performRemove, performLeave)
       } else {
-        remove()
+        performLeave()
       }
+    } else {
+      performRemove()
     }
+  }
 
-    if (props != null && props.onVnodeUnmounted != null) {
-      queuePostRenderEffect(() => {
-        invokeDirectiveHook(props.onVnodeUnmounted!, parentComponent, vnode)
-      }, parentSuspense)
+  function removeChildren(children: HostVNode[]) {
+    for (let i = 0; i < children.length; i++) {
+      remove(children[i])
     }
   }