From 4b7f76e36a7fc650986a20eca258f7a5d912424f Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 2 Nov 2021 13:20:31 +0800 Subject: [PATCH] fix(custom-element): fix custom element props access on initial render ref: #4792 --- .../__tests__/customElement.spec.ts | 24 +++++++++++++++---- packages/runtime-dom/src/apiCustomElement.ts | 23 +++++++++++------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts index 60b51de3893..777f9677d04 100644 --- a/packages/runtime-dom/__tests__/customElement.spec.ts +++ b/packages/runtime-dom/__tests__/customElement.spec.ts @@ -191,13 +191,21 @@ describe('defineCustomElement', () => { test('handling properties set before upgrading', () => { const E = defineCustomElement({ - props: ['foo'], + props: { + foo: String, + dataAge: Number + }, + setup(props) { + expect(props.foo).toBe('hello') + expect(props.dataAge).toBe(5) + }, render() { return `foo: ${this.foo}` } }) const el = document.createElement('my-el-upgrade') as any el.foo = 'hello' + el.dataset.age = 5 container.appendChild(el) customElements.define('my-el-upgrade', E) expect(el.shadowRoot.innerHTML).toBe(`foo: hello`) @@ -363,10 +371,10 @@ describe('defineCustomElement', () => { // should inject styles expect(e1.shadowRoot!.innerHTML).toBe( - `
hello
` + `
hello
` ) expect(e2.shadowRoot!.innerHTML).toBe( - `
world
` + `
world
` ) // attr @@ -374,7 +382,7 @@ describe('defineCustomElement', () => { await nextTick() expect((e1 as any).msg).toBe('attr') expect(e1.shadowRoot!.innerHTML).toBe( - `
attr
` + `
attr
` ) // props @@ -382,7 +390,7 @@ describe('defineCustomElement', () => { ;(e1 as any).msg = 'prop' expect(e1.getAttribute('msg')).toBe('prop') expect(e1.shadowRoot!.innerHTML).toBe( - `
prop
` + `
prop
` ) }) @@ -391,6 +399,9 @@ describe('defineCustomElement', () => { defineAsyncComponent(() => { return Promise.resolve({ props: ['msg'], + setup(props) { + expect(typeof props.msg).toBe('string') + }, render(this: any) { return h('div', this.msg) } @@ -429,6 +440,9 @@ describe('defineCustomElement', () => { defineAsyncComponent(() => { return Promise.resolve({ props: { n: Number }, + setup(props) { + expect(props.n).toBe(20) + }, render(this: any) { return h('div', this.n + ',' + typeof this.n) } diff --git a/packages/runtime-dom/src/apiCustomElement.ts b/packages/runtime-dom/src/apiCustomElement.ts index a532da4c6c7..5127e4a47f5 100644 --- a/packages/runtime-dom/src/apiCustomElement.ts +++ b/packages/runtime-dom/src/apiCustomElement.ts @@ -180,7 +180,6 @@ export class VueElement extends BaseClass { this._connected = true if (!this._instance) { this._resolveDef() - this._update() } } @@ -231,17 +230,15 @@ export class VueElement extends BaseClass { } } } - if (numberProps) { - this._numberProps = numberProps - this._update() - } + this._numberProps = numberProps // check if there are props set pre-upgrade or connect for (const key of Object.keys(this)) { if (key[0] !== '_') { - this._setProp(key, this[key as keyof this]) + this._setProp(key, this[key as keyof this], true, false) } } + // defining getter/setters on prototype for (const key of rawKeys.map(camelize)) { Object.defineProperty(this, key, { @@ -253,7 +250,12 @@ export class VueElement extends BaseClass { } }) } + + // apply CSS this._applyStyles(styles) + + // initial render + this._update() } const asyncDef = (this._def as ComponentOptions).__asyncLoader @@ -282,10 +284,15 @@ export class VueElement extends BaseClass { /** * @internal */ - protected _setProp(key: string, val: any, shouldReflect = true) { + protected _setProp( + key: string, + val: any, + shouldReflect = true, + shouldUpdate = true + ) { if (val !== this._props[key]) { this._props[key] = val - if (this._instance) { + if (shouldUpdate && this._instance) { this._update() } // reflect