fix: ensure vnode hooks are called consistently regardless of keep-alive

This commit is contained in:
Evan You
2020-07-01 19:50:04 -04:00
parent c9629f2692
commit 4e8e689572
3 changed files with 63 additions and 33 deletions

View File

@@ -23,7 +23,8 @@ import {
queuePostRenderEffect,
MoveType,
RendererElement,
RendererNode
RendererNode,
invokeVNodeHook
} from '../renderer'
import { setTransitionHooks } from './BaseTransition'
import { ComponentRenderContext } from '../componentProxy'
@@ -96,11 +97,11 @@ const KeepAliveImpl = {
const storageContainer = createElement('div')
sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
const child = vnode.component!
const instance = vnode.component!
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
// in case props have changed
patch(
child.vnode,
instance.vnode,
vnode,
container,
anchor,
@@ -110,27 +111,35 @@ const KeepAliveImpl = {
optimized
)
queuePostRenderEffect(() => {
child.isDeactivated = false
if (child.a) {
invokeArrayFns(child.a)
instance.isDeactivated = false
if (instance.a) {
invokeArrayFns(instance.a)
}
const vnodeHook = vnode.props && vnode.props.onVnodeMounted
if (vnodeHook) {
invokeVNodeHook(vnodeHook, instance.parent, vnode)
}
}, parentSuspense)
}
sharedContext.deactivate = (vnode: VNode) => {
const instance = vnode.component!
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
queuePostRenderEffect(() => {
const component = vnode.component!
if (component.da) {
invokeArrayFns(component.da)
if (instance.da) {
invokeArrayFns(instance.da)
}
component.isDeactivated = true
const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted
if (vnodeHook) {
invokeVNodeHook(vnodeHook, instance.parent, vnode)
}
instance.isDeactivated = true
}, parentSuspense)
}
function unmount(vnode: VNode) {
// reset the shapeFlag so it can be properly unmounted
vnode.shapeFlag = ShapeFlags.STATEFUL_COMPONENT
resetShapeFlag(vnode)
_unmount(vnode, instance, parentSuspense)
}
@@ -150,7 +159,7 @@ const KeepAliveImpl = {
} else if (current) {
// current active instance should no longer be kept-alive.
// we can't unmount it now but it might be later, so reset its flag now.
current.shapeFlag = ShapeFlags.STATEFUL_COMPONENT
resetShapeFlag(current)
}
cache.delete(key)
keys.delete(key)
@@ -165,7 +174,18 @@ const KeepAliveImpl = {
)
onBeforeUnmount(() => {
cache.forEach(unmount)
cache.forEach(cached => {
const { subTree, suspense } = instance
if (cached.type === subTree.type) {
// current instance will be unmounted as part of keep-alive's unmount
resetShapeFlag(subTree)
// but invoke its deactivated hook here
const da = subTree.component!.da
da && queuePostRenderEffect(da, suspense)
return
}
unmount(cached)
})
})
return () => {
@@ -197,7 +217,7 @@ const KeepAliveImpl = {
(include && (!name || !matches(include, name))) ||
(exclude && name && matches(exclude, name))
) {
return vnode
return (current = vnode)
}
const key = vnode.key == null ? comp : vnode.key
@@ -325,3 +345,14 @@ function injectToKeepAliveRoot(
remove(keepAliveRoot[type]!, hook)
}, target)
}
function resetShapeFlag(vnode: VNode) {
let shapeFlag = vnode.shapeFlag
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
shapeFlag -= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
}
if (shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
shapeFlag -= ShapeFlags.COMPONENT_KEPT_ALIVE
}
vnode.shapeFlag = shapeFlag
}