]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: vdom interop
authordaiwei <daiwei521@126.com>
Fri, 11 Apr 2025 09:04:27 +0000 (17:04 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 11 Apr 2025 09:04:27 +0000 (17:04 +0800)
packages/runtime-vapor/src/block.ts
packages/runtime-vapor/src/component.ts
packages/runtime-vapor/src/components/KeepAlive.ts
packages/runtime-vapor/src/vdomInterop.ts

index c9d81986b45bab3d600e9ccbcbadfcd3c20b9adb..9693bb1adc9602dd0b8f8063642efc22b872b4cd 100644 (file)
@@ -57,10 +57,7 @@ export class DynamicFragment extends VaporFragment {
     // teardown previous branch
     if (this.scope) {
       if (isKeepAlive(instance)) {
-        ;(instance as KeepAliveInstance).process(
-          this.nodes as VaporComponentInstance,
-        )
-        unmountComponent(this.nodes as VaporComponentInstance)
+        ;(instance as KeepAliveInstance).process(this.nodes)
       } else {
         this.scope.stop()
       }
@@ -71,9 +68,7 @@ export class DynamicFragment extends VaporFragment {
       this.scope = new EffectScope()
       this.nodes = this.scope.run(render) || []
       if (isKeepAlive(instance)) {
-        ;(instance as KeepAliveInstance).process(
-          this.nodes as VaporComponentInstance,
-        )
+        ;(instance as KeepAliveInstance).process(this.nodes)
       }
       if (parent) insert(this.nodes, parent, this.anchor)
     } else {
index 957ad1f99f123cd299c16a284b4ea0905610c4a3..278bde8ab2ca5d58dfb85581154d0b857692a7dc 100644 (file)
@@ -154,10 +154,10 @@ export function createComponent(
 
   // try to get the cached instance if inside keep-alive
   if (currentInstance && isKeepAlive(currentInstance)) {
-    const cached = (currentInstance as KeepAliveInstance).getCachedInstance(
+    const cached = (currentInstance as KeepAliveInstance).getCachedComponent(
       component,
     )
-    if (cached) return cached
+    if (cached) return cached as any
   }
 
   // vdom interop enabled and component is not an explicit vapor component
@@ -517,9 +517,8 @@ export function mountComponent(
     ;(instance.parent as KeepAliveInstance).activate(
       instance,
       parentNode,
-      anchor as any,
+      anchor,
     )
-    instance.isMounted = true
     return
   }
 
index b89a559fe676b72e41f75f9ceb902854d765c98c..439f0f3ff8968f08db94de0bb4df2e7b1e9d4ad5 100644 (file)
@@ -12,13 +12,18 @@ import {
   warn,
   watch,
 } from '@vue/runtime-dom'
-import { type Block, insert, isFragment } from '../block'
+import {
+  type Block,
+  type VaporFragment,
+  insert,
+  isFragment,
+  remove,
+} from '../block'
 import {
   type ObjectVaporComponent,
   type VaporComponent,
   type VaporComponentInstance,
   isVaporComponent,
-  unmountComponent,
 } from '../component'
 import { defineVaporComponent } from '../apiDefineComponent'
 import { ShapeFlags, invokeArrayFns, isArray } from '@vue/shared'
@@ -26,19 +31,19 @@ import { createElement } from '../dom/node'
 
 export interface KeepAliveInstance extends VaporComponentInstance {
   activate: (
-    instance: VaporComponentInstance,
+    block: VaporComponentInstance | VaporFragment,
     parentNode: ParentNode,
-    anchor: Node,
+    anchor?: Node | null | 0,
   ) => void
-  deactivate: (instance: VaporComponentInstance) => void
-  process: (instance: VaporComponentInstance) => void
-  getCachedInstance: (
+  deactivate: (block: VaporComponentInstance | VaporFragment) => void
+  process: (block: Block) => void
+  getCachedComponent: (
     comp: VaporComponent,
-  ) => VaporComponentInstance | undefined
+  ) => VaporComponentInstance | VaporFragment | undefined
 }
 
 type CacheKey = VaporComponent
-type Cache = Map<CacheKey, VaporComponentInstance>
+type Cache = Map<CacheKey, VaporComponentInstance | VaporFragment>
 type Keys = Set<CacheKey>
 
 export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
@@ -58,7 +63,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
     const cache: Cache = new Map()
     const keys: Keys = new Set()
     const storageContainer = createElement('div')
-    let current: VaporComponentInstance | undefined
+    let current: VaporComponentInstance | VaporFragment | undefined
 
     if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
       ;(keepAliveInstance as any).__v_cache = cache
@@ -76,7 +81,8 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
     function cacheBlock() {
       const { max } = props
       // TODO suspense
-      const innerBlock = getInnerBlock(keepAliveInstance.block!)!
+      const block = keepAliveInstance.block!
+      const innerBlock = getInnerBlock(block)!
       if (!innerBlock || !shouldCache(innerBlock)) return
 
       const key = innerBlock.type
@@ -91,32 +97,43 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
           pruneCacheEntry(keys.values().next().value!)
         }
       }
-      cache.set(key, (current = innerBlock))
+      cache.set(
+        key,
+        (current =
+          isFragment(block) && isFragment(block.nodes)
+            ? // cache the fragment nodes for vdom interop
+              block.nodes
+            : innerBlock),
+      )
     }
 
     onMounted(cacheBlock)
     onUpdated(cacheBlock)
 
     onBeforeUnmount(() => {
-      cache.forEach(cached => {
+      cache.forEach(item => {
+        const cached = getInnerComponent(item)!
         resetShapeFlag(cached)
         cache.delete(cached.type)
         // current instance will be unmounted as part of keep-alive's unmount
-        if (current && current.type === cached.type) {
-          const da = cached.da
-          da && queuePostFlushCb(da)
-          return
+        if (current) {
+          const innerComp = getInnerComponent(current)!
+          if (innerComp.type === cached.type) {
+            const da = cached.da
+            da && queuePostFlushCb(da)
+            return
+          }
         }
-        unmountComponent(cached, storageContainer)
+        remove(cached, storageContainer)
       })
     })
 
-    keepAliveInstance.getCachedInstance = (comp: VaporComponent) =>
-      cache.get(comp)
+    keepAliveInstance.getCachedComponent = comp => cache.get(comp)
+
+    const process = (keepAliveInstance.process = block => {
+      const instance = getInnerComponent(block)
+      if (!instance) return
 
-    const process = (keepAliveInstance.process = (
-      instance: VaporComponentInstance,
-    ) => {
       if (cache.has(instance.type)) {
         instance.shapeFlag! |= ShapeFlags.COMPONENT_KEPT_ALIVE
       }
@@ -126,13 +143,19 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
       }
     })
 
-    keepAliveInstance.activate = (
-      instance: VaporComponentInstance,
-      parentNode: ParentNode,
-      anchor: Node,
-    ) => {
-      current = instance
-      insert(instance.block, parentNode, anchor)
+    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
+      }
+
       queuePostFlushCb(() => {
         instance.isDeactivated = false
         if (instance.a) invokeArrayFns(instance.a)
@@ -143,8 +166,18 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
       }
     }
 
-    keepAliveInstance.deactivate = (instance: VaporComponentInstance) => {
-      insert(instance.block, storageContainer)
+    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
+      }
+
       queuePostFlushCb(() => {
         if (instance.da) invokeArrayFns(instance.da)
         instance.isDeactivated = true
@@ -171,6 +204,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
 
     function pruneCache(filter: (name: string) => boolean) {
       cache.forEach((instance, key) => {
+        instance = getInnerComponent(instance)!
         const name = getComponentName(instance.type)
         if (name && !filter(name)) {
           pruneCacheEntry(key)
@@ -184,7 +218,7 @@ export const VaporKeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
         resetShapeFlag(cached)
         // don't unmount if the instance is the current one
         if (cached !== current) {
-          unmountComponent(cached)
+          remove(cached)
         }
       }
       cache.delete(key)
@@ -210,7 +244,23 @@ function getInnerBlock(block: Block): VaporComponentInstance | undefined {
   if (isVaporComponent(block)) {
     return block
   }
+  if (isVdomInteropFragment(block)) {
+    return block.nodes as any
+  }
   if (isFragment(block)) {
     return getInnerBlock(block.nodes)
   }
 }
+
+function getInnerComponent(block: Block): VaporComponentInstance | undefined {
+  if (isVaporComponent(block)) {
+    return block
+  } else if (isVdomInteropFragment(block)) {
+    // vdom interop
+    return block.nodes as any
+  }
+}
+
+function isVdomInteropFragment(block: Block): block is VaporFragment {
+  return !!(isFragment(block) && block.insert)
+}
index 77228fd72a02fe85a5496daf7d89bc37e197a4d2..145c81e123e228c22ba81e8f519387e86a1c9b75 100644 (file)
@@ -27,12 +27,13 @@ import {
   unmountComponent,
 } from './component'
 import { type Block, VaporFragment, insert, remove } from './block'
-import { EMPTY_OBJ, extend, isFunction } from '@vue/shared'
+import { EMPTY_OBJ, ShapeFlags, extend, isFunction } from '@vue/shared'
 import { type RawProps, rawPropsProxyHandlers } from './componentProps'
 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'
 
 // mounting vapor components and slots in vdom
 const vaporInteropImpl: Omit<
@@ -172,10 +173,18 @@ function createVDOMComponent(
   let isMounted = false
   const parentInstance = currentInstance as VaporComponentInstance
   const unmount = (parentNode?: ParentNode) => {
+    if (vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
+      ;(parentInstance as KeepAliveInstance).deactivate(frag)
+      return
+    }
     internals.umt(vnode.component!, null, !!parentNode)
   }
 
   frag.insert = (parentNode, anchor) => {
+    if (vnode.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
+      ;(parentInstance as KeepAliveInstance).activate(frag, parentNode, anchor)
+      return
+    }
     if (!isMounted) {
       internals.mt(
         vnode,
@@ -201,6 +210,7 @@ function createVDOMComponent(
   }
 
   frag.remove = unmount
+  frag.nodes = vnode as any
 
   return frag
 }