]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: vdom interop
authordaiwei <daiwei521@126.com>
Mon, 14 Apr 2025 07:34:19 +0000 (15:34 +0800)
committerdaiwei <daiwei521@126.com>
Mon, 14 Apr 2025 08:16:32 +0000 (16:16 +0800)
packages/runtime-core/src/apiCreateApp.ts
packages/runtime-core/src/components/KeepAlive.ts
packages/runtime-core/src/renderer.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/components/KeepAlive.ts
packages/runtime-vapor/src/vdomInterop.ts

index d8ae73fb69d1bbf3614458c8651d202c55ed50f9..d088fd9be108ab48eebeee3f5fed90e26b38a53f 100644 (file)
@@ -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
index 9cce1ef4d93dc59014850a8ef3815f6eef32ba06..731da28b8f526708b4bd4ed1114ac0d96745bae0 100644 (file)
@@ -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,
index 5a18d62a8e1b2d6f74bdd814217bf482e4fa8ac2..d3ac10e581eb111cd426ae46fa83ed15eb89872e 100644 (file)
@@ -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
index 278bde8ab2ca5d58dfb85581154d0b857692a7dc..f1e526106fe4c7849c103ddaf270deb19b0ab4a2 100644 (file)
@@ -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
   }
index cafefb1f1bfe574c59c2fbf5e9b6afa1aabc7fc0..3c097f6696d15e456a554696ddd985b76f372fad 100644 (file)
@@ -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)
+  }
+}
index 753cc50dc115c5769c49d4865723291b3b13a0de..0da6ebdbd21afe17b19c5fdd5cfd0c7aaf8c99f8 100644 (file)
@@ -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,