From 376883d1cfea6ed92807cce1f1209f943a04b625 Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 22 Sep 2020 11:38:03 -0400 Subject: [PATCH] fix(runtime-core): fix duplicated unmount traversal in optimized mode fix #2169 --- .../__tests__/rendererOptimizedMode.spec.ts | 28 +++++++++++++++++- packages/runtime-core/src/renderer.ts | 29 +++++++++++++++---- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts b/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts index 73e566c930e..8b674b8a395 100644 --- a/packages/runtime-core/__tests__/rendererOptimizedMode.spec.ts +++ b/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('

1

') }) + + // #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) + }) }) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 64d2d3c379f..bb3129bd7af 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/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) } }