daiwei 1 год назад
Родитель
Сommit
ea34f2f555

+ 0 - 3
packages/runtime-core/src/apiCreateApp.ts

@@ -33,7 +33,6 @@ import type { NormalizedPropsOptions } from './componentProps'
 import type { ObjectEmitsOptions } from './componentEmits'
 import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
 import type { DefineComponent } from './apiDefineComponent'
-import type { createHydrationFunctions } from './hydration'
 
 export interface App<HostElement = any> {
   version: string
@@ -105,7 +104,6 @@ export interface App<HostElement = any> {
   _container: HostElement | null
   _context: AppContext
   _instance: GenericComponentInstance | null
-  _ssr?: boolean
 
   /**
    * @internal custom element vnode
@@ -206,7 +204,6 @@ export interface VaporInteropInterface {
     parentComponent: any, // VaporComponentInstance
     fallback?: any, // VaporSlot
   ) => any
-  vdomHydrate: ReturnType<typeof createHydrationFunctions>[1] | undefined
 }
 
 /**

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

@@ -149,7 +149,6 @@ export const createApp = ((...args) => {
 
 export const createSSRApp = ((...args) => {
   const app = ensureHydrationRenderer().createApp(...args)
-  app._ssr = true
 
   if (__DEV__) {
     injectNativeTagCheck(app)

+ 14 - 9
packages/runtime-vapor/__tests__/hydration.spec.ts

@@ -54,6 +54,14 @@ function compile(
   )
 }
 
+async function testHydrationInterop(
+  code: string,
+  components?: Record<string, string | { code: string; vapor: boolean }>,
+  data?: any,
+) {
+  return testHydration(code, components, data, { interop: true, vapor: false })
+}
+
 async function testHydration(
   code: string,
   components: Record<string, string | { code: string; vapor: boolean }> = {},
@@ -65,7 +73,7 @@ async function testHydration(
   for (const key in components) {
     const comp = components[key]
     const code = isString(comp) ? comp : comp.code
-    const isVaporComp = !isString(comp) ? comp.vapor : true
+    const isVaporComp = isString(comp) || !!comp.vapor
     clientComponents[key] = compile(code, data, clientComponents, {
       vapor: isVaporComp,
       ssr: false,
@@ -3838,9 +3846,9 @@ describe('Vapor Mode hydration', () => {
 })
 
 describe('VDOM hydration interop', () => {
-  test('basic component', async () => {
+  test('basic vapor component', async () => {
     const data = ref(true)
-    const { container } = await testHydration(
+    const { container } = await testHydrationInterop(
       `<script setup>const data = _data; const components = _components;</script>
       <template>
         <components.VaporChild/>
@@ -3852,7 +3860,6 @@ describe('VDOM hydration interop', () => {
         },
       },
       data,
-      { interop: true, vapor: false },
     )
 
     expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
@@ -3864,7 +3871,7 @@ describe('VDOM hydration interop', () => {
 
   test('nested components (VDOM -> Vapor -> VDOM)', async () => {
     const data = ref(true)
-    const { container } = await testHydration(
+    const { container } = await testHydrationInterop(
       `<script setup>const data = _data; const components = _components;</script>
       <template>
         <components.VaporChild/>
@@ -3881,7 +3888,6 @@ describe('VDOM hydration interop', () => {
         },
       },
       data,
-      { interop: true, vapor: false },
     )
 
     expect(container.innerHTML).toMatchInlineSnapshot(`"true"`)
@@ -3891,9 +3897,9 @@ describe('VDOM hydration interop', () => {
     expect(container.innerHTML).toMatchInlineSnapshot(`"false"`)
   })
 
-  test.todo('slots', async () => {
+  test('vapor slot render vdom component', async () => {
     const data = ref(true)
-    const { container } = await testHydration(
+    const { container } = await testHydrationInterop(
       `<script setup>const data = _data; const components = _components;</script>
       <template>
         <components.VaporChild>
@@ -3912,7 +3918,6 @@ describe('VDOM hydration interop', () => {
         },
       },
       data,
-      { interop: true, vapor: false },
     )
 
     expect(container.innerHTML).toMatchInlineSnapshot(

+ 0 - 1
packages/runtime-vapor/src/block.ts

@@ -154,7 +154,6 @@ export function insert(
   } else {
     // fragment
     if (block.insert) {
-      // TODO handle hydration for vdom interop
       block.insert(parent, anchor)
     } else {
       insert(block.nodes, parent, anchor)

+ 5 - 16
packages/runtime-vapor/src/component.ts

@@ -58,11 +58,7 @@ import {
   getSlot,
 } from './componentSlots'
 import { hmrReload, hmrRerender } from './hmr'
-import {
-  currentHydrationNode,
-  isHydrating,
-  locateHydrationNode,
-} from './dom/hydration'
+import { isHydrating, locateHydrationNode } from './dom/hydration'
 import {
   insertionAnchor,
   insertionParent,
@@ -156,22 +152,15 @@ export function createComponent(
 
   // vdom interop enabled and component is not an explicit vapor component
   if (appContext.vapor && !component.__vapor) {
-    const [frag, vnode] = appContext.vapor.vdomMount(
+    const frag = appContext.vapor.vdomMount(
       component as any,
       rawProps,
       rawSlots,
     )
-    if (!isHydrating && _insertionParent) {
+
+    // `frag.insert` handles both hydration and mounting
+    if (_insertionParent) {
       insert(frag, _insertionParent, _insertionAnchor)
-    } else if (isHydrating) {
-      appContext.vapor.vdomHydrate!(
-        currentHydrationNode!,
-        vnode,
-        currentInstance as any,
-        null,
-        null,
-        false,
-      )
     }
     return frag
   }

+ 6 - 2
packages/runtime-vapor/src/componentSlots.ts

@@ -114,7 +114,6 @@ export function createSlot(
     : EMPTY_OBJ
 
   let fragment: DynamicFragment
-
   if (isRef(rawSlots._)) {
     fragment = instance.appContext.vapor!.vdomSlot(
       rawSlots._,
@@ -157,7 +156,12 @@ export function createSlot(
     }
   }
 
-  if (!isHydrating && _insertionParent) {
+  if (
+    _insertionParent &&
+    (!isHydrating ||
+      // for vdom interop fragment, `fragment.insert` handles both hydration and mounting
+      fragment.insert)
+  ) {
     insert(fragment, _insertionParent, _insertionAnchor)
   }
 

+ 67 - 34
packages/runtime-vapor/src/vdomInterop.ts

@@ -35,12 +35,17 @@ import type { RawSlots, VaporSlot } from './componentSlots'
 import { renderEffect } from './renderEffect'
 import { createTextNode } from './dom/node'
 import { optimizePropertyLookup } from './dom/prop'
-import { hydrateNode as vaporHydrateNode } from './dom/hydration'
+import {
+  currentHydrationNode,
+  isHydrating,
+  locateHydrationNode,
+  hydrateNode as vaporHydrateNode,
+} from './dom/hydration'
 
 // mounting vapor components and slots in vdom
 const vaporInteropImpl: Omit<
   VaporInteropInterface,
-  'vdomMount' | 'vdomUnmount' | 'vdomSlot' | 'vdomHydrate'
+  'vdomMount' | 'vdomUnmount' | 'vdomSlot'
 > = {
   mount(vnode, container, anchor, parentComponent) {
     const selfAnchor = (vnode.el = vnode.anchor = createTextNode())
@@ -144,6 +149,8 @@ const vaporSlotsProxyHandler: ProxyHandler<any> = {
   },
 }
 
+let vdomHydrateNode: HydrationRenderer['hydrateNode'] | undefined
+
 /**
  * Mount vdom component in vapor
  */
@@ -152,7 +159,7 @@ function createVDOMComponent(
   component: ConcreteComponent,
   rawProps?: LooseRawProps | null,
   rawSlots?: LooseRawSlots | null,
-): [VaporFragment, VNode] {
+): VaporFragment {
   const frag = new VaporFragment([])
   const vnode = createVNode(
     component,
@@ -181,16 +188,30 @@ function createVDOMComponent(
   }
 
   frag.insert = (parentNode, anchor) => {
-    if (!isMounted) {
-      internals.mt(
-        vnode,
-        parentNode,
-        anchor,
-        parentInstance as any,
-        null,
-        undefined,
-        false,
-      )
+    if (!isMounted || isHydrating) {
+      if (isHydrating) {
+        ;(
+          vdomHydrateNode ||
+          (vdomHydrateNode = ensureHydrationRenderer().hydrateNode!)
+        )(
+          currentHydrationNode!,
+          vnode,
+          parentInstance as any,
+          null,
+          null,
+          false,
+        )
+      } else {
+        internals.mt(
+          vnode,
+          parentNode,
+          anchor,
+          parentInstance as any,
+          null,
+          undefined,
+          false,
+        )
+      }
       onScopeDispose(unmount, true)
       isMounted = true
     } else {
@@ -207,7 +228,7 @@ function createVDOMComponent(
 
   frag.remove = unmount
 
-  return [frag, vnode]
+  return frag
 }
 
 /**
@@ -235,28 +256,43 @@ function renderVDOMSlot(
           isFunction(name) ? name() : name,
           props,
         )
-        if ((vnode.children as any[]).length) {
-          if (fallbackNodes) {
-            remove(fallbackNodes, parentNode)
-            fallbackNodes = undefined
-          }
-          internals.p(
-            oldVNode,
+        if (isHydrating) {
+          locateHydrationNode(true)
+          ;(
+            vdomHydrateNode ||
+            (vdomHydrateNode = ensureHydrationRenderer().hydrateNode!)
+          )(
+            currentHydrationNode!,
             vnode,
-            parentNode,
-            anchor,
             parentComponent as any,
+            null,
+            null,
+            false,
           )
-          oldVNode = vnode
         } else {
-          if (fallback && !fallbackNodes) {
-            // mount fallback
-            if (oldVNode) {
-              internals.um(oldVNode, parentComponent as any, null, true)
+          if ((vnode.children as any[]).length) {
+            if (fallbackNodes) {
+              remove(fallbackNodes, parentNode)
+              fallbackNodes = undefined
+            }
+            internals.p(
+              oldVNode,
+              vnode,
+              parentNode,
+              anchor,
+              parentComponent as any,
+            )
+            oldVNode = vnode
+          } else {
+            if (fallback && !fallbackNodes) {
+              // mount fallback
+              if (oldVNode) {
+                internals.um(oldVNode, parentComponent as any, null, true)
+              }
+              insert((fallbackNodes = fallback(props)), parentNode, anchor)
             }
-            insert((fallbackNodes = fallback(props)), parentNode, anchor)
+            oldVNode = null
           }
-          oldVNode = null
         }
       })
       isMounted = true
@@ -284,14 +320,11 @@ function renderVDOMSlot(
 }
 
 export const vaporInteropPlugin: Plugin = app => {
-  const { internals, hydrateNode } = (
-    app._ssr ? ensureHydrationRenderer() : ensureRenderer()
-  ) as HydrationRenderer
+  const internals = ensureRenderer().internals
   app._context.vapor = extend(vaporInteropImpl, {
     vdomMount: createVDOMComponent.bind(null, internals),
     vdomUnmount: internals.umt,
     vdomSlot: renderVDOMSlot.bind(null, internals),
-    vdomHydrate: hydrateNode,
   })
   const mount = app.mount
   app.mount = ((...args) => {