From: daiwei Date: Mon, 14 Apr 2025 07:34:19 +0000 (+0800) Subject: wip: vdom interop X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6efc7ec3a7e439fca4711a93ad562e685e4f25d6;p=thirdparty%2Fvuejs%2Fcore.git wip: vdom interop --- diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index d8ae73fb69..d088fd9be1 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -193,6 +193,13 @@ export interface VaporInteropInterface { unmount(vnode: VNode, doRemove?: boolean): void move(vnode: VNode, container: any, anchor: any): void slot(n1: VNode | null, n2: VNode, container: any, anchor: any): void + activate( + vnode: VNode, + container: any, + anchor: any, + parentComponent: ComponentInternalInstance, + ): void + deactivate(vnode: VNode, container: any): void vdomMount: (component: ConcreteComponent, props?: any, slots?: any) => any vdomUnmount: UnmountComponentFn diff --git a/packages/runtime-core/src/components/KeepAlive.ts b/packages/runtime-core/src/components/KeepAlive.ts index 9cce1ef4d9..731da28b8f 100644 --- a/packages/runtime-core/src/components/KeepAlive.ts +++ b/packages/runtime-core/src/components/KeepAlive.ts @@ -72,6 +72,8 @@ export interface KeepAliveContext extends ComponentRenderContext { optimized: boolean, ) => void deactivate: (vnode: VNode) => void + getCachedComponent: (vnode: VNode) => VNode + getStorageContainer: () => RendererElement } export const isKeepAlive = (vnode: any): boolean => @@ -126,6 +128,14 @@ const KeepAliveImpl: ComponentOptions = { } = renderer const storageContainer = createElement('div') + sharedContext.getStorageContainer = () => storageContainer + + sharedContext.getCachedComponent = (vnode: VNode) => { + const key = + vnode.key == null ? (vnode.type as ConcreteComponent) : vnode.key + return cache.get(key)! + } + sharedContext.activate = ( vnode, container, diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 5a18d62a8e..d3ac10e581 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1164,12 +1164,21 @@ function baseCreateRenderer( if ((n2.type as ConcreteComponent).__vapor) { if (n1 == null) { - getVaporInterface(parentComponent, n2).mount( - n2, - container, - anchor, - parentComponent, - ) + if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { + getVaporInterface(parentComponent, n2).activate( + n2, + container, + anchor, + parentComponent!, + ) + } else { + getVaporInterface(parentComponent, n2).mount( + n2, + container, + anchor, + parentComponent, + ) + } } else { getVaporInterface(parentComponent, n2).update( n1, @@ -2178,7 +2187,14 @@ function baseCreateRenderer( } if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { - ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode) + if ((vnode.type as ConcreteComponent).__vapor) { + getVaporInterface(parentComponent!, vnode).deactivate( + vnode, + (parentComponent!.ctx as KeepAliveContext).getStorageContainer(), + ) + } else { + ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode) + } return } @@ -2425,7 +2441,7 @@ function baseCreateRenderer( const getNextHostNode: NextFn = vnode => { if (vnode.shapeFlag & ShapeFlags.COMPONENT) { if ((vnode.type as ConcreteComponent).__vapor) { - return hostNextSibling((vnode.component! as any).block) + return hostNextSibling(vnode.anchor!) } return getNextHostNode(vnode.component!.subTree) } @@ -2620,9 +2636,11 @@ export function traverseStaticChildren( } function locateNonHydratedAsyncRoot( - instance: ComponentInternalInstance, + instance: GenericComponentInstance, ): ComponentInternalInstance | undefined { - const subComponent = instance.subTree.component + const subComponent = instance.vapor + ? null + : (instance as ComponentInternalInstance).subTree.component if (subComponent) { if (subComponent.asyncDep && !subComponent.asyncResolved) { return subComponent diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts index 278bde8ab2..f1e526106f 100644 --- a/packages/runtime-vapor/src/component.ts +++ b/packages/runtime-vapor/src/component.ts @@ -152,12 +152,17 @@ export function createComponent( locateHydrationNode() } - // try to get the cached instance if inside keep-alive - if (currentInstance && isKeepAlive(currentInstance)) { + // keep-alive + if ( + currentInstance && + currentInstance.vapor && + isKeepAlive(currentInstance) + ) { const cached = (currentInstance as KeepAliveInstance).getCachedComponent( component, ) - if (cached) return cached as any + // @ts-expect-error cached may be a fragment + if (cached) return cached } // vdom interop enabled and component is not an explicit vapor component @@ -167,6 +172,8 @@ export function createComponent( rawProps, rawSlots, ) + // TODO: problem is `frag.insert` will be called multiple times + // if used in v-if if (!isHydrating && _insertionParent) { insert(frag, _insertionParent, _insertionAnchor) } @@ -544,7 +551,12 @@ export function unmountComponent( instance: VaporComponentInstance, parentNode?: ParentNode, ): void { - if (instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { + if ( + parentNode && + instance.parent && + instance.parent.vapor && + instance.shapeFlag! & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE + ) { ;(instance.parent as KeepAliveInstance).deactivate(instance) return } diff --git a/packages/runtime-vapor/src/components/KeepAlive.ts b/packages/runtime-vapor/src/components/KeepAlive.ts index cafefb1f1b..3c097f6696 100644 --- a/packages/runtime-vapor/src/components/KeepAlive.ts +++ b/packages/runtime-vapor/src/components/KeepAlive.ts @@ -147,29 +147,11 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({ keepAliveInstance.activate = (instance, parentNode, anchor) => { current = instance - insert(instance.block, parentNode, anchor) - - queuePostFlushCb(() => { - instance.isDeactivated = false - if (instance.a) invokeArrayFns(instance.a) - }) - - if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { - devtoolsComponentAdded(instance) - } + activate(instance, parentNode, anchor) } keepAliveInstance.deactivate = instance => { - insert(instance.block, storageContainer) - - queuePostFlushCb(() => { - if (instance.da) invokeArrayFns(instance.da) - instance.isDeactivated = true - }) - - if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { - devtoolsComponentAdded(instance) - } + deactivate(instance, storageContainer) } let children = slots.default() @@ -248,3 +230,36 @@ function getInnerComponent(block: Block): VaporComponentInstance | undefined { function isVdomInteropFragment(block: Block): block is VaporFragment { return !!(isFragment(block) && block.insert) } + +export function activate( + instance: VaporComponentInstance, + parentNode: ParentNode, + anchor?: Node | null | 0, +): void { + insert(instance.block, parentNode, anchor) + + queuePostFlushCb(() => { + instance.isDeactivated = false + if (instance.a) invokeArrayFns(instance.a) + }) + + if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { + devtoolsComponentAdded(instance) + } +} + +export function deactivate( + instance: VaporComponentInstance, + container: ParentNode, +): void { + insert(instance.block, container) + + queuePostFlushCb(() => { + if (instance.da) invokeArrayFns(instance.da) + instance.isDeactivated = true + }) + + if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { + devtoolsComponentAdded(instance) + } +} diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index 753cc50dc1..0da6ebdbd2 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -9,15 +9,16 @@ import { type Slots, type VNode, type VaporInteropInterface, - activate, createVNode, currentInstance, - deactivate, ensureRenderer, + isKeepAlive, onScopeDispose, renderSlot, shallowRef, simpleSetCurrentInstance, + activate as vdomActivate, + deactivate as vdomDeactivate, } from '@vue/runtime-dom' import { type LooseRawProps, @@ -35,7 +36,12 @@ import type { RawSlots, VaporSlot } from './componentSlots' import { renderEffect } from './renderEffect' import { createTextNode } from './dom/node' import { optimizePropertyLookup } from './dom/prop' -import type { KeepAliveInstance } from './components/KeepAlive' +import { + type KeepAliveInstance, + activate, + deactivate, +} from './components/KeepAlive' +import type { KeepAliveContext } from 'packages/runtime-core/src/components/KeepAlive' // mounting vapor components and slots in vdom const vaporInteropImpl: Omit< @@ -63,6 +69,8 @@ const vaporInteropImpl: Omit< )) instance.rawPropsRef = propsRef instance.rawSlotsRef = slotsRef + // copy the shape flag from the vdom component if inside a keep-alive + if (isKeepAlive(parentComponent)) instance.shapeFlag = vnode.shapeFlag mountComponent(instance, container, selfAnchor) simpleSetCurrentInstance(prev) return instance @@ -116,6 +124,23 @@ const vaporInteropImpl: Omit< insert(vnode.vb || (vnode.component as any), container, anchor) insert(vnode.anchor as any, container, anchor) }, + + activate(vnode, container, anchor, parentComponent) { + const cached = (parentComponent.ctx as KeepAliveContext).getCachedComponent( + vnode, + ) + + vnode.el = cached.el + vnode.component = cached.component + vnode.anchor = cached.anchor + activate(vnode.component as any, container, anchor) + insert(vnode.anchor as any, container, anchor) + }, + + deactivate(vnode, container) { + deactivate(vnode.component as any, container) + insert(vnode.anchor as any, container) + }, } const vaporSlotPropsProxyHandler: ProxyHandler< @@ -176,7 +201,7 @@ function createVDOMComponent( const parentInstance = currentInstance as VaporComponentInstance const unmount = (parentNode?: ParentNode) => { if (vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { - deactivate( + vdomDeactivate( vnode, (parentInstance as KeepAliveInstance).getStorageContainer(), internals, @@ -190,7 +215,7 @@ function createVDOMComponent( frag.insert = (parentNode, anchor) => { if (vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { - activate( + vdomActivate( vnode, parentNode, anchor,