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

fix #2169
This commit is contained in:
Evan You 2020-09-22 11:38:03 -04:00
parent 5dbd6b36a0
commit 376883d1cf
2 changed files with 50 additions and 7 deletions

View File

@ -14,7 +14,8 @@ import {
nextTick, nextTick,
defineComponent, defineComponent,
withCtx, withCtx,
renderSlot renderSlot,
onBeforeUnmount
} from '@vue/runtime-test' } from '@vue/runtime-test'
import { PatchFlags, SlotFlags } from '@vue/shared' import { PatchFlags, SlotFlags } from '@vue/shared'
@ -449,4 +450,29 @@ describe('renderer: optimized mode', () => {
expect(inner(root)).toBe('<div><p>1</p></div>') 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)
})
}) })

View File

@ -213,7 +213,8 @@ type UnmountFn = (
vnode: VNode, vnode: VNode,
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
doRemove?: boolean doRemove?: boolean,
optimized?: boolean
) => void ) => void
type RemoveFn = (vnode: VNode) => void type RemoveFn = (vnode: VNode) => void
@ -223,6 +224,7 @@ type UnmountChildrenFn = (
parentComponent: ComponentInternalInstance | null, parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null, parentSuspense: SuspenseBoundary | null,
doRemove?: boolean, doRemove?: boolean,
optimized?: boolean,
start?: number start?: number
) => void ) => void
@ -1647,7 +1649,14 @@ function baseCreateRenderer(
} }
if (oldLength > newLength) { if (oldLength > newLength) {
// remove old // remove old
unmountChildren(c1, parentComponent, parentSuspense, true, commonLength) unmountChildren(
c1,
parentComponent,
parentSuspense,
true,
false,
commonLength
)
} else { } else {
// mount new // mount new
mountChildren( mountChildren(
@ -1968,7 +1977,8 @@ function baseCreateRenderer(
vnode, vnode,
parentComponent, parentComponent,
parentSuspense, parentSuspense,
doRemove = false doRemove = false,
optimized = false
) => { ) => {
const { const {
type, type,
@ -2016,8 +2026,14 @@ function baseCreateRenderer(
(patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT)) (patchFlag > 0 && patchFlag & PatchFlags.STABLE_FRAGMENT))
) { ) {
// fast path for block nodes: only need to unmount dynamic children. // fast path for block nodes: only need to unmount dynamic children.
unmountChildren(dynamicChildren, parentComponent, parentSuspense) unmountChildren(
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) { dynamicChildren,
parentComponent,
parentSuspense,
false,
true
)
} else if (!optimized && shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(children as VNode[], parentComponent, parentSuspense) unmountChildren(children as VNode[], parentComponent, parentSuspense)
} }
@ -2149,10 +2165,11 @@ function baseCreateRenderer(
parentComponent, parentComponent,
parentSuspense, parentSuspense,
doRemove = false, doRemove = false,
optimized = false,
start = 0 start = 0
) => { ) => {
for (let i = start; i < children.length; i++) { for (let i = start; i < children.length; i++) {
unmount(children[i], parentComponent, parentSuspense, doRemove) unmount(children[i], parentComponent, parentSuspense, doRemove, optimized)
} }
} }