]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(vapor): decouple KeepAlive from AsyncComponent via reactive __asyncResolved...
authoredison <daiwei521@126.com>
Mon, 12 Jan 2026 08:44:01 +0000 (16:44 +0800)
committerGitHub <noreply@github.com>
Mon, 12 Jan 2026 08:44:01 +0000 (16:44 +0800)
packages/runtime-vapor/src/apiDefineAsyncComponent.ts
packages/runtime-vapor/src/components/KeepAlive.ts

index da8cb9096005e1075abb21e87b1187aa56787744..4c63ca2141a851d4394632f8b9716f3dcbe7ad82 100644 (file)
@@ -5,9 +5,9 @@ import {
   createAsyncComponentContext,
   currentInstance,
   handleError,
-  isKeepAlive,
   markAsyncBoundary,
   performAsyncHydrate,
+  shallowRef,
   useAsyncComponentState,
   watch,
 } from '@vue/runtime-dom'
@@ -30,7 +30,6 @@ import { invokeArrayFns } from '@vue/shared'
 import { type TransitionOptions, insert, remove } from './block'
 import { parentNode } from './dom/node'
 import { setTransitionHooks } from './components/Transition'
-import type { KeepAliveInstance } from './components/KeepAlive'
 
 /*@ __NO_SIDE_EFFECTS__ */
 export function defineVaporAsyncComponent<T extends VaporComponent>(
@@ -50,6 +49,8 @@ export function defineVaporAsyncComponent<T extends VaporComponent>(
     },
   } = createAsyncComponentContext<T, VaporComponent>(source)
 
+  const resolvedDef = shallowRef<VaporComponent>()
+
   return defineVaporComponent({
     name: 'VaporAsyncComponentWrapper',
 
@@ -107,8 +108,10 @@ export function defineVaporAsyncComponent<T extends VaporComponent>(
       )
     },
 
+    // this accessor tracks the internal shallowRef `resolvedDef`.
+    // this allows KeepAlive to watch the resolution status.
     get __asyncResolved() {
-      return getResolvedComp()
+      return resolvedDef.value
     },
 
     setup() {
@@ -150,11 +153,8 @@ export function defineVaporAsyncComponent<T extends VaporComponent>(
 
       load()
         .then(() => {
+          resolvedDef.value = getResolvedComp()!
           loaded.value = true
-          // if parent is keep-alive, re-evaluate caching
-          if (instance.parent && isKeepAlive(instance.parent)) {
-            ;(instance.parent as KeepAliveInstance).ctx.onAsyncResolve(instance)
-          }
         })
         .catch(err => {
           onError(err)
index a27bc0aecc5d0fed77e5868526fcd2f0295a9443..df595c541b92a3eb15cde6f7b1de16a588ad337c 100644 (file)
@@ -48,7 +48,6 @@ export interface KeepAliveInstance extends VaporComponentInstance {
       comp: VaporComponent,
     ) => VaporComponentInstance | VaporFragment | undefined
     getStorageContainer: () => ParentNode
-    onAsyncResolve: (asyncWrapper: VaporComponentInstance) => void
   }
 }
 
@@ -91,13 +90,6 @@ const KeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
         current = undefined
         deactivate(instance, storageContainer)
       },
-      // called when async component resolves to evaluate caching
-      onAsyncResolve: (asyncWrapper: VaporComponentInstance) => {
-        if (shouldCache(asyncWrapper, props, false)) {
-          asyncWrapper.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
-          innerCacheBlock(asyncWrapper.type, asyncWrapper)
-        }
-      },
     }
 
     const innerCacheBlock = (
@@ -250,11 +242,26 @@ const KeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
       }
     }
 
+    // for unresolved async wrapper, we need to watch the __asyncResolved
+    // property and cache the resolved component once it resolves.
+    const watchAsyncResolve = (instance: VaporComponentInstance) => {
+      if (!instance.type.__asyncResolved) {
+        watch(
+          () => instance.type.__asyncResolved,
+          resolved => {
+            if (resolved) cacheBlock()
+          },
+          { once: true },
+        )
+      }
+    }
+
     // process shapeFlag
     if (isVaporComponent(children)) {
       children.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
       if (isAsyncWrapper(children)) {
         injectKeepAliveHooks(children.block as DynamicFragment)
+        watchAsyncResolve(children)
       }
     } else if (isInteropFragment(children)) {
       children.vnode!.shapeFlag! |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
@@ -263,6 +270,7 @@ const KeepAliveImpl: ObjectVaporComponent = defineVaporComponent({
       injectKeepAliveHooks(children)
       if (isVaporComponent(children.nodes) && isAsyncWrapper(children.nodes)) {
         injectKeepAliveHooks(children.nodes.block as DynamicFragment)
+        watchAsyncResolve(children.nodes)
       }
     }
 
@@ -286,7 +294,7 @@ const shouldCache = (
   ) as GenericComponent & AsyncComponentInternalOptions
 
   // for unresolved async components, don't cache yet
-  // caching will be done in onAsyncResolve after the component resolves
+  // caching will be handled by the watcher in watchAsyncResolve
   if (isAsync && !type.__asyncResolved) {
     return false
   }