]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: enhance VaporElement with type parameters and pre-rendering support
authordaiwei <daiwei521@126.com>
Fri, 24 Oct 2025 07:10:10 +0000 (15:10 +0800)
committerdaiwei <daiwei521@126.com>
Fri, 24 Oct 2025 07:10:10 +0000 (15:10 +0800)
packages/runtime-dom/src/apiCustomElement.ts
packages/runtime-vapor/src/apiDefineVaporCustomElement.ts

index b8e5811700d29671062a26cd29366a6e59e54200..91ba8e606021503ef175e4c125816301ba5dd38e 100644 (file)
@@ -201,6 +201,8 @@ const BaseClass = (
 type InnerComponentDef = ConcreteComponent & CustomElementOptions
 
 export abstract class VueElementBase<
+    E = Element,
+    C = Component,
     Def extends CustomElementOptions & { props?: any } = InnerComponentDef,
   >
   extends BaseClass
@@ -218,7 +220,7 @@ export abstract class VueElementBase<
   /**
    * @internal
    */
-  _root: Element | ShadowRoot
+  _root!: Element | ShadowRoot
   /**
    * @internal
    */
@@ -230,7 +232,7 @@ export abstract class VueElementBase<
 
   protected _def: Def
   protected _props: Record<string, any>
-  protected _createApp: CreateAppFunction<Element>
+  protected _createApp: CreateAppFunction<E, C>
   protected _connected = false
   protected _resolved = false
   protected _numberProps: Record<string, true> | null = null
@@ -249,29 +251,27 @@ export abstract class VueElementBase<
   protected _ob?: MutationObserver | null = null
   protected _slots?: Record<string, Node[]>
 
+  protected abstract _hasPreRendered(): boolean | undefined
+  protected abstract _mountComponent(def: Def): void
+  protected abstract _updateComponent(): void
+  protected abstract _unmountComponent(): void
+
   constructor(
     /**
      * Component def - note this may be an AsyncWrapper, and this._def will
      * be overwritten by the inner component when resolved.
      */
     def: Def,
-    props: Record<string, any> = {},
-    createAppFn: CreateAppFunction<Element> = createApp,
+    props: Record<string, any> | undefined = {},
+    createAppFn: CreateAppFunction<E, C>,
   ) {
     super()
     this._def = def
     this._props = props
     this._createApp = createAppFn
     this._nonce = def.nonce
-    if (this.shadowRoot && createAppFn !== createApp) {
-      this._root = this.shadowRoot
-    } else {
-      if (__DEV__ && this.shadowRoot) {
-        warn(
-          `Custom element has pre-rendered declarative shadow root but is not ` +
-            `defined as hydratable. Use \`defineSSRCustomElement\`.`,
-        )
-      }
+
+    if (this._hasPreRendered()) {
       if (def.shadowRoot !== false) {
         this.attachShadow(
           extend({}, def.shadowRootOptions, {
@@ -322,10 +322,6 @@ export abstract class VueElementBase<
     }
   }
 
-  protected abstract _mountComponent(def: Def): void
-  protected abstract _updateComponent(): void
-  protected abstract _unmountComponent(): void
-
   protected _setParent(
     parent: VueElementBase | undefined = this._parent,
   ): void {
@@ -643,7 +639,33 @@ export abstract class VueElementBase<
   }
 }
 
-export class VueElement extends VueElementBase<InnerComponentDef> {
+export class VueElement extends VueElementBase<
+  Element,
+  Component,
+  InnerComponentDef
+> {
+  constructor(
+    def: InnerComponentDef,
+    props: Record<string, any> | undefined = {},
+    createAppFn: CreateAppFunction<Element, Component> = createApp,
+  ) {
+    super(def, props, createAppFn)
+  }
+
+  protected _hasPreRendered(): boolean | undefined {
+    if (this.shadowRoot && this._createApp !== createApp) {
+      this._root = this.shadowRoot
+    } else {
+      if (__DEV__ && this.shadowRoot) {
+        warn(
+          `Custom element has pre-rendered declarative shadow root but is not ` +
+            `defined as hydratable. Use \`defineSSRCustomElement\`.`,
+        )
+      }
+      return true
+    }
+  }
+
   protected _mountComponent(def: InnerComponentDef): void {
     if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && !def.name) {
       // @ts-expect-error
index bda741a0b7f164b79b497d95f2821e356405227a..fa443268ab029baeffc60931d43c322d7d40548f 100644 (file)
@@ -1,9 +1,10 @@
 import { extend, isPlainObject } from '@vue/shared'
-import { defineVaporComponent } from '.'
+import { createVaporApp, defineVaporComponent } from '.'
 import {
   type CreateAppFunction,
   type CustomElementOptions,
   VueElementBase,
+  warn,
 } from '@vue/runtime-dom'
 import type { ObjectVaporComponent, VaporComponent } from './component'
 
@@ -14,13 +15,13 @@ export type VaporElementConstructor<P = {}> = {
 // TODO type inference
 
 /*@__NO_SIDE_EFFECTS__*/
-export function defineCustomElement(
+export function defineVaporCustomElement(
   options: any,
   extraOptions?: Omit<ObjectVaporComponent, 'setup'>,
   /**
    * @internal
    */
-  _createApp?: CreateAppFunction<Element>,
+  _createApp?: CreateAppFunction<ParentNode, VaporComponent>,
 ): VaporElementConstructor {
   let Comp = defineVaporComponent(options, extraOptions)
   if (isPlainObject(Comp)) Comp = extend({}, Comp, extraOptions)
@@ -34,9 +35,43 @@ export function defineCustomElement(
   return VaporCustomElement
 }
 
+/*@__NO_SIDE_EFFECTS__*/
+export const defineVaporSSRCustomElement = ((
+  options: any,
+  extraOptions?: Omit<ObjectVaporComponent, 'setup'>,
+) => {
+  // @ts-expect-error
+  return defineVaporCustomElement(options, extraOptions, createVaporSSRApp)
+}) as typeof defineVaporCustomElement
+
 type VaporInnerComponentDef = VaporComponent & CustomElementOptions
 
-export class VaporElement extends VueElementBase<VaporInnerComponentDef> {
+export class VaporElement extends VueElementBase<
+  ParentNode,
+  VaporComponent,
+  VaporInnerComponentDef
+> {
+  constructor(
+    def: VaporInnerComponentDef,
+    props: Record<string, any> | undefined = {},
+    createAppFn: CreateAppFunction<ParentNode, VaporComponent> = createVaporApp,
+  ) {
+    super(def, props, createAppFn)
+  }
+
+  protected _hasPreRendered(): boolean | undefined {
+    if (this.shadowRoot && this._createApp !== createVaporApp) {
+      this._root = this.shadowRoot
+    } else {
+      if (__DEV__ && this.shadowRoot) {
+        warn(
+          `Custom element has pre-rendered declarative shadow root but is not ` +
+            `defined as hydratable. Use \`defineVaporSSRCustomElement\`.`,
+        )
+      }
+      return true
+    }
+  }
   protected _mountComponent(def: VaporInnerComponentDef): void {
     throw new Error('Method not implemented.')
   }