Sfoglia il codice sorgente

fix(runtime-core): fix duplicated unmount traversal in optimized mode

fix #2169
Evan You 5 anni fa
parent
commit
376883d1cf

+ 27 - 1
packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts

@@ -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)
+  })
 })

+ 23 - 6
packages/runtime-core/src/renderer.ts

@@ -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)
     }
   }