Explorar el Código

fix(runtime-vapor): invoke vnode update hooks on KeepAlive reactivation

daiwei hace 1 mes
padre
commit
446b21b5e4

+ 1 - 0
packages/runtime-core/src/index.ts

@@ -688,6 +688,7 @@ export type { GenericComponent } from './component'
 export {
 export {
   warnExtraneousAttributes,
   warnExtraneousAttributes,
   getFunctionalFallthrough,
   getFunctionalFallthrough,
+  shouldUpdateComponent,
 } from './componentRenderUtils'
 } from './componentRenderUtils'
 
 
 /**
 /**

+ 58 - 0
packages/runtime-vapor/__tests__/vdomInterop.spec.ts

@@ -2077,5 +2077,63 @@ describe('vdomInterop', () => {
       await nextTick()
       await nextTick()
       expect(vnodeMounted).toHaveBeenCalledTimes(2)
       expect(vnodeMounted).toHaveBeenCalledTimes(2)
     })
     })
+
+    test('should invoke onVnodeBeforeUpdate/onVnodeUpdated on reactivation', async () => {
+      const VaporChild = defineVaporComponent({
+        props: ['msg'],
+        setup(props: any) {
+          return template('<div></div>')()
+        },
+      })
+
+      const VdomChild = defineComponent({
+        setup() {
+          return () => h('span', 'vdom')
+        },
+      })
+
+      const current = shallowRef<any>(VaporChild)
+      const msg = ref('hello')
+      const beforeUpdateSpy = vi.fn()
+      const updatedSpy = vi.fn()
+
+      const App = defineComponent({
+        setup() {
+          return () =>
+            h(KeepAlive, null, {
+              default: () =>
+                h(
+                  resolveDynamicComponent(current.value) as any,
+                  current.value === VaporChild
+                    ? {
+                        msg: msg.value,
+                        onVnodeBeforeUpdate: beforeUpdateSpy,
+                        onVnodeUpdated: updatedSpy,
+                      }
+                    : null,
+                ),
+            })
+        },
+      })
+
+      const root = document.createElement('div')
+      const app = createApp(App)
+      app.use(vaporInteropPlugin)
+      app.mount(root)
+      await nextTick()
+
+      // Deactivate vapor child
+      current.value = VdomChild
+      await nextTick()
+
+      // Change props while deactivated
+      msg.value = 'world'
+
+      // Reactivate — should trigger update hooks
+      current.value = VaporChild
+      await nextTick()
+      expect(beforeUpdateSpy).toHaveBeenCalledTimes(1)
+      expect(updatedSpy).toHaveBeenCalledTimes(1)
+    })
   })
   })
 })
 })

+ 29 - 3
packages/runtime-vapor/src/vdomInterop.ts

@@ -39,6 +39,7 @@ import {
   setTransitionHooks as setVNodeTransitionHooks,
   setTransitionHooks as setVNodeTransitionHooks,
   shallowReactive,
   shallowReactive,
   shallowRef,
   shallowRef,
+  shouldUpdateComponent,
   simpleSetCurrentInstance,
   simpleSetCurrentInstance,
   activate as vdomActivate,
   activate as vdomActivate,
   deactivate as vdomDeactivate,
   deactivate as vdomDeactivate,
@@ -381,16 +382,41 @@ const vaporInteropImpl: Omit<
     vnode.component = cached.component
     vnode.component = cached.component
     vnode.anchor = cached.anchor
     vnode.anchor = cached.anchor
     const instance = vnode.component as any as VaporComponentInstance
     const instance = vnode.component as any as VaporComponentInstance
+    const shouldUpdate = shouldUpdateComponent(cached, vnode)
     activate(instance, container, anchor)
     activate(instance, container, anchor)
     insert(vnode.anchor as any, container, anchor)
     insert(vnode.anchor as any, container, anchor)
     // in case props have changed while deactivated
     // in case props have changed while deactivated
     instance.rawPropsRef!.value = filterReservedProps(vnode.props)
     instance.rawPropsRef!.value = filterReservedProps(vnode.props)
     instance.rawSlotsRef!.value = vnode.children
     instance.rawSlotsRef!.value = vnode.children
+    if (shouldUpdate) {
+      const vnodeBeforeUpdateHook =
+        vnode.props && vnode.props.onVnodeBeforeUpdate
+      if (vnodeBeforeUpdateHook) {
+        callWithAsyncErrorHandling(
+          vnodeBeforeUpdateHook,
+          parentComponent,
+          ErrorCodes.VNODE_HOOK,
+          [vnode, cached],
+        )
+      }
+    }
     queuePostFlushCb(() => {
     queuePostFlushCb(() => {
-      const vnodeHook = vnode.props && vnode.props.onVnodeMounted
-      if (vnodeHook) {
+      if (shouldUpdate) {
+        const vnodeUpdatedHook = vnode.props && vnode.props.onVnodeUpdated
+        if (vnodeUpdatedHook) {
+          callWithAsyncErrorHandling(
+            vnodeUpdatedHook,
+            parentComponent,
+            ErrorCodes.VNODE_HOOK,
+            [vnode, cached],
+          )
+        }
+      }
+
+      const vnodeMountedHook = vnode.props && vnode.props.onVnodeMounted
+      if (vnodeMountedHook) {
         callWithAsyncErrorHandling(
         callWithAsyncErrorHandling(
-          vnodeHook,
+          vnodeMountedHook,
           parentComponent,
           parentComponent,
           ErrorCodes.VNODE_HOOK,
           ErrorCodes.VNODE_HOOK,
           [vnode],
           [vnode],