]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(runtime-core): fix duplicated unmount traversal in optimized mode
authorEvan You <yyx990803@gmail.com>
Tue, 22 Sep 2020 15:38:03 +0000 (11:38 -0400)
committerEvan You <yyx990803@gmail.com>
Tue, 22 Sep 2020 15:38:15 +0000 (11:38 -0400)
fix #2169

packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts
packages/runtime-core/src/renderer.ts

index 73e566c930e9bc59359173943d4ed593a724aec6..8b674b8a3957af0623835eec3cb933db1e512e87 100644 (file)
@@ -14,7 +14,8 @@ import {
   nextTick,
   defineComponent,
   withCtx,
-  renderSlot
+  renderSlot,
+  onBeforeUnmount
 } from '@vue/runtime-test'
 import { PatchFlags, SlotFlags } from '@vue/shared'
 
@@ -449,4 +450,29 @@ describe('renderer: optimized mode', () => {
 
     expect(inner(root)).toBe('<div><p>1</p></div>')
   })
+
+  // #2169
+  // block
+  // - dynamic child (1)
+  //   - component (2)
+  // When unmounting (1), we know we are in optimized mode so no need to further
+  // traverse unmount its children
+  test('should not perform unnecessary unmount traversals', () => {
+    const spy = jest.fn()
+    const Child = {
+      setup() {
+        onBeforeUnmount(spy)
+        return () => 'child'
+      }
+    }
+    const Parent = () => (
+      openBlock(),
+      createBlock('div', null, [
+        createVNode('div', { style: {} }, [createVNode(Child)], 4 /* STYLE */)
+      ])
+    )
+    render(h(Parent), root)
+    render(null, root)
+    expect(spy).toHaveBeenCalledTimes(1)
+  })
 })
index 64d2d3c379ff075f39a76e64eeac5175d389a7f1..bb3129bd7af612379fc98fb7a8bd93d39d3a23e9 100644 (file)
@@ -213,7 +213,8 @@ type UnmountFn = (
   vnode: VNode,
   parentComponent: ComponentInternalInstance | null,
   parentSuspense: SuspenseBoundary | null,
-  doRemove?: boolean
+  doRemove?: boolean,
+  optimized?: boolean
 ) => void
 
 type RemoveFn = (vnode: VNode) => void
@@ -223,6 +224,7 @@ type UnmountChildrenFn = (
   parentComponent: ComponentInternalInstance | null,
   parentSuspense: SuspenseBoundary | null,
   doRemove?: boolean,
+  optimized?: boolean,
   start?: number
 ) => void
 
@@ -1647,7 +1649,14 @@ function baseCreateRenderer(
     }
     if (oldLength > newLength) {
       // remove old
-      unmountChildren(c1, parentComponent, parentSuspense, true, commonLength)
+      unmountChildren(
+        c1,
+        parentComponent,
+        parentSuspense,
+        true,
+        false,
+        commonLength
+      )
     } else {
       // mount new
       mountChildren(
@@ -1968,7 +1977,8 @@ function baseCreateRenderer(
     vnode,
     parentComponent,
     parentSuspense,
-    doRemove = false
+    doRemove = false,
+    optimized = false
   ) => {
     const {
       type,
@@ -2016,8 +2026,14 @@ function baseCreateRenderer(
           (patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT))
       ) {
         // fast path for block nodes: only need to unmount dynamic children.
-        unmountChildren(dynamicChildren, parentComponent, parentSuspense)
-      } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
+        unmountChildren(
+          dynamicChildren,
+          parentComponent,
+          parentSuspense,
+          false,
+          true
+        )
+      } else if (!optimized && shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
         unmountChildren(children as VNode[], parentComponent, parentSuspense)
       }
 
@@ -2149,10 +2165,11 @@ function baseCreateRenderer(
     parentComponent,
     parentSuspense,
     doRemove = false,
+    optimized = false,
     start = 0
   ) => {
     for (let i = start; i < children.length; i++) {
-      unmount(children[i], parentComponent, parentSuspense, doRemove)
+      unmount(children[i], parentComponent, parentSuspense, doRemove, optimized)
     }
   }