wip: vdom interop

This commit is contained in:
daiwei 2025-04-14 10:15:25 +08:00
parent 6c4d22ac73
commit eeb6ab886a
4 changed files with 128 additions and 86 deletions

View File

@ -1,4 +1,5 @@
import {
type ComponentInternalInstance,
type ComponentOptions,
type ConcreteComponent,
type GenericComponentInstance,
@ -46,7 +47,7 @@ import { setTransitionHooks } from './BaseTransition'
import type { ComponentRenderContext } from '../componentPublicInstance'
import { devtoolsComponentAdded } from '../devtools'
import { isAsyncWrapper } from '../apiAsyncComponent'
import { isSuspense } from './Suspense'
import { type SuspenseBoundary, isSuspense } from './Suspense'
import { LifecycleHooks } from '../enums'
type MatchPattern = string | RegExp | (string | RegExp)[]
@ -118,14 +119,11 @@ const KeepAliveImpl: ComponentOptions = {
const parentSuspense = keepAliveInstance.suspense
const { renderer } = sharedContext
const {
renderer: {
p: patch,
m: move,
um: _unmount,
o: { createElement },
},
} = sharedContext
um: _unmount,
o: { createElement },
} = renderer
const storageContainer = createElement('div')
sharedContext.activate = (
@ -135,72 +133,26 @@ const KeepAliveImpl: ComponentOptions = {
namespace,
optimized,
) => {
const instance = vnode.component!
move(
activate(
vnode,
container,
anchor,
MoveType.ENTER,
renderer,
keepAliveInstance,
parentSuspense,
)
// in case props have changed
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
namespace,
vnode.slotScopeIds,
optimized,
)
queuePostRenderEffect(() => {
instance.isDeactivated = false
if (instance.a) {
invokeArrayFns(instance.a)
}
const vnodeHook = vnode.props && vnode.props.onVnodeMounted
if (vnodeHook) {
invokeVNodeHook(vnodeHook, instance.parent, vnode)
}
}, parentSuspense)
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// Update components tree
devtoolsComponentAdded(instance)
}
}
sharedContext.deactivate = (vnode: VNode) => {
const instance = vnode.component!
invalidateMount(instance.m)
invalidateMount(instance.a)
move(
deactivate(
vnode,
storageContainer,
null,
MoveType.LEAVE,
renderer,
keepAliveInstance,
parentSuspense,
)
queuePostRenderEffect(() => {
if (instance.da) {
invokeArrayFns(instance.da)
}
const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted
if (vnodeHook) {
invokeVNodeHook(vnodeHook, instance.parent, vnode)
}
instance.isDeactivated = true
}, parentSuspense)
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// Update components tree
devtoolsComponentAdded(instance)
}
}
function unmount(vnode: VNode) {
@ -487,3 +439,86 @@ export function resetShapeFlag(vnode: any): void {
function getInnerChild(vnode: VNode) {
return vnode.shapeFlag & ShapeFlags.SUSPENSE ? vnode.ssContent! : vnode
}
/**
* shared between runtime-core and runtime-vapor
*/
export function activate(
vnode: VNode,
container: RendererElement,
anchor: RendererNode | null,
{ p: patch, m: move }: RendererInternals,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
namespace?: ElementNamespace,
optimized?: boolean,
): void {
const instance = vnode.component!
move(
vnode,
container,
anchor,
MoveType.ENTER,
parentComponent,
parentSuspense,
)
// in case props have changed
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
namespace,
vnode.slotScopeIds,
optimized,
)
queuePostRenderEffect(() => {
instance.isDeactivated = false
if (instance.a) {
invokeArrayFns(instance.a)
}
const vnodeHook = vnode.props && vnode.props.onVnodeMounted
if (vnodeHook) {
invokeVNodeHook(vnodeHook, instance.parent, vnode)
}
}, parentSuspense)
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// Update components tree
devtoolsComponentAdded(instance)
}
}
/**
* shared between runtime-core and runtime-vapor
*/
export function deactivate(
vnode: VNode,
container: RendererElement,
{ m: move }: RendererInternals,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
): void {
const instance = vnode.component!
invalidateMount(instance.m)
invalidateMount(instance.a)
move(vnode, container, null, MoveType.LEAVE, parentComponent, parentSuspense)
queuePostRenderEffect(() => {
if (instance.da) {
invokeArrayFns(instance.da)
}
const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted
if (vnodeHook) {
invokeVNodeHook(vnodeHook, instance.parent, vnode)
}
instance.isDeactivated = true
}, parentSuspense)
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// Update components tree
devtoolsComponentAdded(instance)
}
}

View File

@ -564,7 +564,13 @@ export { getComponentName } from './component'
/**
* @internal
*/
export { matches, isKeepAlive, resetShapeFlag } from './components/KeepAlive'
export {
matches,
isKeepAlive,
resetShapeFlag,
activate,
deactivate,
} from './components/KeepAlive'
/**
* @internal
*/

View File

@ -31,15 +31,16 @@ import { createElement } from '../dom/node'
export interface KeepAliveInstance extends VaporComponentInstance {
activate: (
block: VaporComponentInstance | VaporFragment,
instance: VaporComponentInstance,
parentNode: ParentNode,
anchor?: Node | null | 0,
) => void
deactivate: (block: VaporComponentInstance | VaporFragment) => void
deactivate: (instance: VaporComponentInstance) => void
process: (block: Block) => void
getCachedComponent: (
comp: VaporComponent,
) => VaporComponentInstance | VaporFragment | undefined
getStorageContainer: () => ParentNode
}
type CacheKey = VaporComponent
@ -128,6 +129,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
})
})
keepAliveInstance.getStorageContainer = () => storageContainer
keepAliveInstance.getCachedComponent = comp => cache.get(comp)
const process = (keepAliveInstance.process = block => {
@ -143,18 +145,9 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
}
})
keepAliveInstance.activate = (block, parentNode, anchor) => {
current = block
let instance
if (isVaporComponent(block)) {
instance = block
insert(block.block, parentNode, anchor)
} else {
// vdom interop
const comp = block.nodes as any
insert(comp.el, parentNode, anchor)
instance = comp.component
}
keepAliveInstance.activate = (instance, parentNode, anchor) => {
current = instance
insert(instance.block, parentNode, anchor)
queuePostFlushCb(() => {
instance.isDeactivated = false
@ -166,17 +159,8 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
}
}
keepAliveInstance.deactivate = block => {
let instance
if (isVaporComponent(block)) {
instance = block
insert(block.block, storageContainer)
} else {
// vdom interop
const comp = block.nodes as any
insert(comp.el, storageContainer)
instance = comp.component
}
keepAliveInstance.deactivate = instance => {
insert(instance.block, storageContainer)
queuePostFlushCb(() => {
if (instance.da) invokeArrayFns(instance.da)

View File

@ -9,8 +9,10 @@ import {
type Slots,
type VNode,
type VaporInteropInterface,
activate,
createVNode,
currentInstance,
deactivate,
ensureRenderer,
onScopeDispose,
renderSlot,
@ -174,7 +176,13 @@ function createVDOMComponent(
const parentInstance = currentInstance as VaporComponentInstance
const unmount = (parentNode?: ParentNode) => {
if (vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
;(parentInstance as KeepAliveInstance).deactivate(frag)
deactivate(
vnode,
(parentInstance as KeepAliveInstance).getStorageContainer(),
internals,
parentInstance as any,
null,
)
return
}
internals.umt(vnode.component!, null, !!parentNode)
@ -182,7 +190,16 @@ function createVDOMComponent(
frag.insert = (parentNode, anchor) => {
if (vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
;(parentInstance as KeepAliveInstance).activate(frag, parentNode, anchor)
activate(
vnode,
parentNode,
anchor,
internals,
parentInstance as any,
null,
undefined,
false,
)
return
}
if (!isMounted) {