From a07e7bf5536a6b3db70ba9bb1c3f366dac1bf5a0 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 8 Aug 2024 16:26:48 +0800 Subject: [PATCH] fix(custom-element): support early-set domProps for async custom elements close #11081 close #11082 --- .../__tests__/customElement.spec.ts | 35 +++++++++++++++++++ packages/runtime-dom/src/apiCustomElement.ts | 9 ++--- packages/runtime-dom/src/patchProp.ts | 12 ++++++- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 38af0e2a63e..da0d614f657 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -1206,4 +1206,39 @@ describe('defineCustomElement', () => { 'hello', ) }) + + // #11081 + test('Props can be casted when mounting custom elements in component rendering functions', async () => { + const E = defineCustomElement( + defineAsyncComponent(() => + Promise.resolve({ + props: ['fooValue'], + setup(props) { + expect(props.fooValue).toBe('fooValue') + return () => h('div', props.fooValue) + }, + }), + ), + ) + customElements.define('my-el-async-4', E) + const R = defineComponent({ + setup() { + const fooValue = ref('fooValue') + return () => { + return h('div', null, [ + h('my-el-async-4', { + fooValue: fooValue.value, + }), + ]) + } + }, + }) + + const app = createApp(R) + app.mount(container) + await new Promise(r => setTimeout(r)) + const e = container.querySelector('my-el-async-4') as VueElement + expect(e.shadowRoot!.innerHTML).toBe(`
fooValue
`) + app.unmount() + }) }) diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index af6063cdb02..aa191ba6c7c 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -200,6 +200,7 @@ export class VueElement extends BaseClass implements ComponentCustomElementInterface { + _isVueCE = true /** * @internal */ @@ -208,6 +209,10 @@ export class VueElement * @internal */ _app: App | null = null + /** + * @internal + */ + _root: Element | ShadowRoot /** * @internal */ @@ -228,10 +233,6 @@ export class VueElement */ private _childStyles?: Map private _ob?: MutationObserver | null = null - /** - * @internal - */ - public _root: Element | ShadowRoot private _slots?: Record constructor( diff --git a/packages/runtime-dom/src/patchProp.ts b/packages/runtime-dom/src/patchProp.ts index f3ef14ee83c..98b69967c71 100644 --- a/packages/runtime-dom/src/patchProp.ts +++ b/packages/runtime-dom/src/patchProp.ts @@ -5,6 +5,7 @@ import { patchDOMProp } from './modules/props' import { patchEvent } from './modules/events' import { isFunction, isModelListener, isOn, isString } from '@vue/shared' import type { RendererOptions } from '@vue/runtime-core' +import type { VueElement } from './apiCustomElement' const isNativeOn = (key: string) => key.charCodeAt(0) === 111 /* o */ && @@ -127,5 +128,14 @@ function shouldSetAsProp( return false } - return key in el + if (key in el) { + return true + } + + // #11081 force set props for possible async custom element + if ((el as VueElement)._isVueCE && (/[A-Z]/.test(key) || !isString(value))) { + return true + } + + return false }