From 4b8a7fb729fddad6b459e0385defb60ee341f472 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 24 Oct 2025 17:13:11 +0800 Subject: [PATCH] wip: save --- packages/runtime-core/src/apiCreateApp.ts | 5 ++ packages/runtime-dom/src/apiCustomElement.ts | 88 +++++++++++-------- packages/runtime-vapor/src/apiCreateApp.ts | 36 ++++---- .../src/apiDefineVaporCustomElement.ts | 67 ++++++++++++-- packages/runtime-vapor/src/index.ts | 4 + 5 files changed, 137 insertions(+), 63 deletions(-) diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index f651d76d79..e3a63bf846 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -111,6 +111,11 @@ export interface App { */ _ceVNode?: VNode + /** + * @internal vapor custom element instance + */ + _ceComponent?: GenericComponentInstance | null + /** * v2 compat only */ diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index 91ba8e6060..61c3d7c441 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -252,9 +252,9 @@ export abstract class VueElementBase< protected _slots?: Record protected abstract _hasPreRendered(): boolean | undefined - protected abstract _mountComponent(def: Def): void - protected abstract _updateComponent(): void - protected abstract _unmountComponent(): void + protected abstract _mount(def: Def): void + protected abstract _update(): void + protected abstract _unmount(): void constructor( /** @@ -352,7 +352,7 @@ export abstract class VueElementBase< this._ob.disconnect() this._ob = null } - this._unmountComponent() + this._unmount() if (this._teleportTargets) { this._teleportTargets.clear() this._teleportTargets = undefined @@ -436,6 +436,45 @@ export abstract class VueElementBase< } } + private _mountComponent(def: Def): void { + this._mount(def) + this._processExposed() + } + + protected _processExposed(): void { + const exposed = this._instance && this._instance.exposed + if (!exposed) return + for (const key in exposed) { + if (!hasOwn(this, key)) { + Object.defineProperty(this, key, { + get: () => unref(exposed[key]), + }) + } else if (__DEV__) { + warn(`Exposed property "${key}" already exists on custom element.`) + } + } + } + + protected _processEmit(): void { + const dispatch = (event: string, args: any[]) => { + this.dispatchEvent( + new CustomEvent( + event, + isPlainObject(args[0]) + ? extend({ detail: args }, args[0]) + : { detail: args }, + ), + ) + } + + this._instance!.emit = (event: string, ...args: any[]) => { + dispatch(event, args) + if (hyphenate(event) !== event) { + dispatch(hyphenate(event), args) + } + } + } + private _resolveProps(def: Def): void { const { props } = def const declaredPropKeys = isArray(props) ? props : Object.keys(props || {}) @@ -498,7 +537,7 @@ export abstract class VueElementBase< } } if (shouldUpdate && this._instance) { - this._updateComponent() + this._update() } // reflect if (shouldReflect) { @@ -666,7 +705,7 @@ export class VueElement extends VueElementBase< } } - protected _mountComponent(def: InnerComponentDef): void { + protected _mount(def: InnerComponentDef): void { if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && !def.name) { // @ts-expect-error def.name = 'VueElement' @@ -678,28 +717,16 @@ export class VueElement extends VueElementBase< } this._app._ceVNode = this._createVNode() this._app.mount(this._root) - - const exposed = this._instance && this._instance.exposed - if (!exposed) return - for (const key in exposed) { - if (!hasOwn(this, key)) { - Object.defineProperty(this, key, { - get: () => unref(exposed[key]), - }) - } else if (__DEV__) { - warn(`Exposed property "${key}" already exists on custom element.`) - } - } } - protected _updateComponent(): void { + protected _update(): void { if (!this._app) return const vnode = this._createVNode() vnode.appContext = this._app._context render(vnode, this._root) } - protected _unmountComponent(): void { + protected _unmount(): void { if (this._app) { this._app.unmount() } @@ -729,28 +756,11 @@ export class VueElement extends VueElementBase< } this._applyStyles(newStyles) this._instance = null - this._updateComponent() - } - } - - const dispatch = (event: string, args: any[]) => { - this.dispatchEvent( - new CustomEvent( - event, - isPlainObject(args[0]) - ? extend({ detail: args }, args[0]) - : { detail: args }, - ), - ) - } - - instance.emit = (event: string, ...args: any[]) => { - dispatch(event, args) - if (hyphenate(event) !== event) { - dispatch(hyphenate(event), args) + this._update() } } + this._processEmit() this._setParent() } } diff --git a/packages/runtime-vapor/src/apiCreateApp.ts b/packages/runtime-vapor/src/apiCreateApp.ts index 89fc6179ee..a50c12e60e 100644 --- a/packages/runtime-vapor/src/apiCreateApp.ts +++ b/packages/runtime-vapor/src/apiCreateApp.ts @@ -36,14 +36,16 @@ const mountApp: AppMountFn = (app, container) => { container.textContent = '' } - const instance = createComponent( - app._component, - app._props as RawProps, - null, - false, - false, - app._context, - ) + const instance = + (app._ceComponent as VaporComponentInstance) || + createComponent( + app._component, + app._props as RawProps, + null, + false, + false, + app._context, + ) mountComponent(instance, container) flushOnAppMount() @@ -57,14 +59,16 @@ const hydrateApp: AppMountFn = (app, container) => { let instance: VaporComponentInstance withHydration(container, () => { - instance = createComponent( - app._component, - app._props as RawProps, - null, - false, - false, - app._context, - ) + instance = + (app._ceComponent as VaporComponentInstance) || + createComponent( + app._component, + app._props as RawProps, + null, + false, + false, + app._context, + ) mountComponent(instance, container) flushOnAppMount() }) diff --git a/packages/runtime-vapor/src/apiDefineVaporCustomElement.ts b/packages/runtime-vapor/src/apiDefineVaporCustomElement.ts index fa443268ab..8cc1782c44 100644 --- a/packages/runtime-vapor/src/apiDefineVaporCustomElement.ts +++ b/packages/runtime-vapor/src/apiDefineVaporCustomElement.ts @@ -1,12 +1,18 @@ import { extend, isPlainObject } from '@vue/shared' -import { createVaporApp, defineVaporComponent } from '.' +import { createComponent, createVaporApp, defineVaporComponent } from '.' import { type CreateAppFunction, type CustomElementOptions, VueElementBase, warn, } from '@vue/runtime-dom' -import type { ObjectVaporComponent, VaporComponent } from './component' +import { + type ObjectVaporComponent, + type VaporComponent, + type VaporComponentInstance, + mountComponent, + unmountComponent, +} from './component' export type VaporElementConstructor

= { new (initialProps?: Record): VaporElement & P @@ -72,13 +78,58 @@ export class VaporElement extends VueElementBase< return true } } - protected _mountComponent(def: VaporInnerComponentDef): void { - throw new Error('Method not implemented.') + protected _mount(def: VaporInnerComponentDef): void { + if ((__DEV__ || __FEATURE_PROD_DEVTOOLS__) && !def.name) { + def.name = 'VaporElement' + } + + this._app = this._createApp(this._def) + this._inheritParentContext() + if (this._def.configureApp) { + this._def.configureApp(this._app) + } + + this._app._ceComponent = this._createComponent() + this._app!.mount(this._root) + } + + protected _update(): void { + if (!this._app) return + unmountComponent(this._instance! as VaporComponentInstance, this._root) + const instance = this._createComponent() + instance.appContext = this._app!._context + mountComponent(this._instance! as VaporComponentInstance, this._root) } - protected _updateComponent(): void { - throw new Error('Method not implemented.') + + protected _unmount(): void { + this._app!.unmount() + this._app = this._instance = null } - protected _unmountComponent(): void { - throw new Error('Method not implemented.') + + private _createComponent() { + this._instance = createComponent(this._def, this._props) + if (!this.shadowRoot) { + this._instance!.m = this._instance!.u = [this._renderSlots.bind(this)] + } + + this._instance.ce = this + this._instance.isCE = true + + if (__DEV__) { + this._instance.ceReload = newStyles => { + if (this._styles) { + this._styles.forEach(s => this._root.removeChild(s)) + this._styles.length = 0 + } + this._applyStyles(newStyles) + this._instance = null + this._update() + } + } + + this._processEmit() + this._setParent() + + return this._instance } } diff --git a/packages/runtime-vapor/src/index.ts b/packages/runtime-vapor/src/index.ts index 0d718747d2..d8fbbf0e97 100644 --- a/packages/runtime-vapor/src/index.ts +++ b/packages/runtime-vapor/src/index.ts @@ -6,6 +6,10 @@ export { vaporInteropPlugin } from './vdomInterop' export type { VaporDirective } from './directives/custom' export { VaporTeleportImpl as VaporTeleport } from './components/Teleport' export { VaporKeepAliveImpl as VaporKeepAlive } from './components/KeepAlive' +export { + defineVaporCustomElement, + defineVaporSSRCustomElement, +} from './apiDefineVaporCustomElement' // compiler-use only export { insert, prepend, remove } from './block' -- 2.47.3