]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: reuse code from BaseTransition
authordaiwei <daiwei521@126.com>
Fri, 28 Feb 2025 01:45:04 +0000 (09:45 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 28 Feb 2025 01:45:04 +0000 (09:45 +0800)
packages/runtime-core/src/components/BaseTransition.ts
packages/runtime-core/src/index.ts
packages/runtime-vapor/src/components/Transition.ts

index 673d30a777aea3bd88d210c1d4f8fbd36af2a31d..c59fd7338b1211efa665d534d7920f944b8976d1 100644 (file)
@@ -25,7 +25,7 @@ import { SchedulerJobFlags } from '../scheduler'
 
 type Hook<T = () => void> = T | T[]
 
-const leaveCbKey: unique symbol = Symbol('_leaveCb')
+export const leaveCbKey: unique symbol = Symbol('_leaveCb')
 const enterCbKey: unique symbol = Symbol('_enterCb')
 
 export interface BaseTransitionProps<HostElement = RendererElement> {
@@ -88,7 +88,7 @@ export interface TransitionState {
   isUnmounting: boolean
   // Track pending leave callbacks for children of the same key.
   // This is used to force remove leaving a child when a new copy is entering.
-  leavingVNodes: Map<any, Record<string, VNode>>
+  leavingVNodes: Map<any, Record<string, any>>
 }
 
 export interface TransitionElement {
@@ -319,6 +319,13 @@ function getLeavingNodesForType(
   return leavingVNodesCache
 }
 
+export interface TransitionHooksContext {
+  setLeavingNodeCache: () => void
+  unsetLeavingNodeCache: () => void
+  earlyRemove: () => void
+  cloneHooks: (node: any) => TransitionHooks
+}
+
 // The transition hooks are attached to the vnode as vnode.transition
 // and will be called at appropriate timing in the renderer.
 export function resolveTransitionHooks(
@@ -328,6 +335,57 @@ export function resolveTransitionHooks(
   instance: GenericComponentInstance,
   postClone?: (hooks: TransitionHooks) => void,
 ): TransitionHooks {
+  const key = String(vnode.key)
+  const leavingVNodesCache = getLeavingNodesForType(state, vnode)
+  const context: TransitionHooksContext = {
+    setLeavingNodeCache: () => {
+      leavingVNodesCache[key] = vnode
+    },
+    unsetLeavingNodeCache: () => {
+      if (leavingVNodesCache[key] === vnode) {
+        delete leavingVNodesCache[key]
+      }
+    },
+    earlyRemove: () => {
+      const leavingVNode = leavingVNodesCache[key]
+      if (
+        leavingVNode &&
+        isSameVNodeType(vnode, leavingVNode) &&
+        (leavingVNode.el as TransitionElement)[leaveCbKey]
+      ) {
+        // force early removal (not cancelled)
+        ;(leavingVNode.el as TransitionElement)[leaveCbKey]!()
+      }
+    },
+    cloneHooks: vnode => {
+      const hooks = resolveTransitionHooks(
+        vnode,
+        props,
+        state,
+        instance,
+        postClone,
+      )
+      if (postClone) postClone(hooks)
+      return hooks
+    },
+  }
+
+  return baseResolveTransitionHooks(context, props, state, instance)
+}
+
+export function baseResolveTransitionHooks(
+  context: TransitionHooksContext,
+  props: BaseTransitionProps<any>,
+  state: TransitionState,
+  instance: GenericComponentInstance,
+): TransitionHooks {
+  const {
+    setLeavingNodeCache,
+    unsetLeavingNodeCache,
+    earlyRemove,
+    cloneHooks,
+  } = context
+
   const {
     appear,
     mode,
@@ -345,8 +403,6 @@ export function resolveTransitionHooks(
     onAfterAppear,
     onAppearCancelled,
   } = props
-  const key = String(vnode.key)
-  const leavingVNodesCache = getLeavingNodesForType(state, vnode)
 
   const callHook: TransitionHookCaller = (hook, args) => {
     hook &&
@@ -388,16 +444,7 @@ export function resolveTransitionHooks(
         el[leaveCbKey](true /* cancelled */)
       }
       // for toggled element with same key (v-if)
-      const leavingVNode = leavingVNodesCache[key]
-      if (
-        leavingVNode &&
-        isSameVNodeType(vnode, leavingVNode) &&
-        // TODO refactor
-        ((leavingVNode.el || leavingVNode) as TransitionElement)[leaveCbKey]
-      ) {
-        // force early removal (not cancelled)
-        ;((leavingVNode.el || leavingVNode) as TransitionElement)[leaveCbKey]!()
-      }
+      earlyRemove()
       callHook(hook, [el])
     },
 
@@ -436,7 +483,7 @@ export function resolveTransitionHooks(
     },
 
     leave(el, remove) {
-      const key = String(vnode.key)
+      // const key = String(vnode.key)
       if (el[enterCbKey]) {
         el[enterCbKey](true /* cancelled */)
       }
@@ -455,11 +502,9 @@ export function resolveTransitionHooks(
           callHook(onAfterLeave, [el])
         }
         el[leaveCbKey] = undefined
-        if (leavingVNodesCache[key] === vnode) {
-          delete leavingVNodesCache[key]
-        }
+        unsetLeavingNodeCache()
       })
-      leavingVNodesCache[key] = vnode
+      setLeavingNodeCache()
       if (onLeave) {
         callAsyncHook(onLeave, [el, done])
       } else {
@@ -467,16 +512,8 @@ export function resolveTransitionHooks(
       }
     },
 
-    clone(vnode) {
-      const hooks = resolveTransitionHooks(
-        vnode,
-        props,
-        state,
-        instance,
-        postClone,
-      )
-      if (postClone) postClone(hooks)
-      return hooks
+    clone(node) {
+      return cloneHooks(node)
     },
   }
 
index 51f42562eebfed4182aac9860d90d10f412e84ff..9da2b2cba4ba92cd10ef7bc965c42d82fe526101 100644 (file)
@@ -150,8 +150,10 @@ export { registerRuntimeCompiler, isRuntimeOnly } from './component'
 export {
   useTransitionState,
   resolveTransitionHooks,
+  baseResolveTransitionHooks,
   setTransitionHooks,
   getTransitionRawChildren,
+  leaveCbKey,
 } from './components/BaseTransition'
 export { initCustomFormatter } from './customFormatter'
 
@@ -335,6 +337,8 @@ export type { SuspenseBoundary } from './components/Suspense'
 export type {
   TransitionState,
   TransitionHooks,
+  TransitionHooksContext,
+  TransitionElement,
 } from './components/BaseTransition'
 export type {
   AsyncComponentOptions,
index 00cf52414e3f7372ceee795fbb01ee6b7a0f590d..9b9410234b7de5cd47790365d587d72f81dae4bd 100644 (file)
@@ -1,10 +1,15 @@
 import {
+  type GenericComponentInstance,
+  type TransitionElement,
   type TransitionHooks,
+  type TransitionHooksContext,
   type TransitionProps,
+  type TransitionState,
   type VaporTransitionInterface,
+  baseResolveTransitionHooks,
   currentInstance,
+  leaveCbKey,
   registerVaporTransition,
-  resolveTransitionHooks,
   useTransitionState,
 } from '@vue/runtime-dom'
 import type { Block } from '../block'
@@ -64,6 +69,59 @@ export const vaporTransitionImpl: VaporTransitionInterface = {
   },
 }
 
+function resolveTransitionHooks(
+  block: Block & { key: string },
+  props: TransitionProps,
+  state: TransitionState,
+  instance: GenericComponentInstance,
+  postClone?: (hooks: TransitionHooks) => void,
+): TransitionHooks {
+  const key = String(block.key)
+  const leavingNodeCache = getLeavingNodesForBlock(state, block)
+  const context: TransitionHooksContext = {
+    setLeavingNodeCache: () => {
+      leavingNodeCache[key] = block
+    },
+    unsetLeavingNodeCache: () => {
+      if (leavingNodeCache[key] === block) {
+        delete leavingNodeCache[key]
+      }
+    },
+    earlyRemove: () => {
+      const leavingNode = leavingNodeCache[key]
+      if (leavingNode && (leavingNode as TransitionElement)[leaveCbKey]) {
+        // force early removal (not cancelled)
+        ;(leavingNode as TransitionElement)[leaveCbKey]!()
+      }
+    },
+    cloneHooks: block => {
+      const hooks = resolveTransitionHooks(
+        block,
+        props,
+        state,
+        instance,
+        postClone,
+      )
+      if (postClone) postClone(hooks)
+      return hooks
+    },
+  }
+  return baseResolveTransitionHooks(context, props, state, instance)
+}
+
+function getLeavingNodesForBlock(
+  state: TransitionState,
+  block: Block,
+): Record<string, Block> {
+  const { leavingVNodes } = state
+  let leavingNodesCache = leavingVNodes.get(block)!
+  if (!leavingNodesCache) {
+    leavingNodesCache = Object.create(null)
+    leavingVNodes.set(block, leavingNodesCache)
+  }
+  return leavingNodesCache
+}
+
 function setTransitionHooks(block: Block, hooks: TransitionHooks) {
   if (isVaporComponent(block)) {
     setTransitionHooks(block.block, hooks)