diff --git a/.changeset/whole-deer-walk.md b/.changeset/whole-deer-walk.md new file mode 100644 index 0000000000..bb21c6761e --- /dev/null +++ b/.changeset/whole-deer-walk.md @@ -0,0 +1,7 @@ +--- +"@lynx-js/web-mainthread-apis": patch +"@lynx-js/web-constants": patch +"@lynx-js/web-core": patch +--- + +fix: under the all-on-ui strategy, reload() will add two page elements. diff --git a/packages/web-platform/web-constants/src/constants.ts b/packages/web-platform/web-constants/src/constants.ts index 088f60313e..7bc6b61b23 100644 --- a/packages/web-platform/web-constants/src/constants.ts +++ b/packages/web-platform/web-constants/src/constants.ts @@ -16,6 +16,8 @@ export const lynxDatasetAttribute = 'l-dset' as const; export const lynxComponentConfigAttribute = 'l-comp-cfg' as const; +export const lynxDisposedAttribute = 'l-disposed' as const; + export const lynxDefaultDisplayLinearAttribute = 'lynx-default-display-linear' as const; diff --git a/packages/web-platform/web-core/src/apis/LynxView.ts b/packages/web-platform/web-core/src/apis/LynxView.ts index 244b21139d..9c84f1c0a0 100644 --- a/packages/web-platform/web-core/src/apis/LynxView.ts +++ b/packages/web-platform/web-core/src/apis/LynxView.ts @@ -8,6 +8,8 @@ import { } from './createLynxView.js'; import { inShadowRootStyles, + lynxDisposedAttribute, + lynxTagAttribute, type Cloneable, type I18nResourceTranslationOptions, type InitI18nResources, @@ -369,6 +371,13 @@ export class LynxView extends HTMLElement { disconnectedCallback() { this.#instance?.dispose(); this.#instance = undefined; + // under the all-on-ui strategy, when reload() triggers dsl flush, the previously removed pageElement will be used in __FlushElementTree. + // This attribute is added to filter this issue. + this.shadowRoot?.querySelector(`[${lynxTagAttribute}="page"]`) + ?.setAttribute( + lynxDisposedAttribute, + '', + ); if (this.shadowRoot) { this.shadowRoot.innerHTML = ''; } diff --git a/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts b/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts index b2a5265f53..a683862618 100644 --- a/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts +++ b/packages/web-platform/web-mainthread-apis/src/createMainThreadGlobalThis.ts @@ -57,6 +57,7 @@ import { type GetPageElementPAPI, type MinimalRawEventObject, type I18nResourceTranslationOptions, + lynxDisposedAttribute, } from '@lynx-js/web-constants'; import { globalMuteableVars } from '@lynx-js/web-constants'; import { createMainThreadLynx } from './createMainThreadLynx.js'; @@ -604,7 +605,10 @@ export function createMainThreadGlobalThis( ) => { const timingFlagsCopied = timingFlags; timingFlags = []; - if (pageElement && !pageElement.parentNode) { + if ( + pageElement && !pageElement.parentNode + && pageElement.getAttribute(lynxDisposedAttribute) !== '' + ) { // @ts-expect-error rootDom.append(pageElement); } diff --git a/packages/web-platform/web-tests/tests/react.spec.ts b/packages/web-platform/web-tests/tests/react.spec.ts index 1500b96d2e..fee71b2ea5 100644 --- a/packages/web-platform/web-tests/tests/react.spec.ts +++ b/packages/web-platform/web-tests/tests/react.spec.ts @@ -87,6 +87,23 @@ test.describe('reactlynx3 tests', () => { await wait(100); await expect(await target.getAttribute('style')).toContain('pink'); }); + test('basic-reload-page-only-one', async ({ page }, { title }) => { + await goto(page, 'basic-reload'); + await wait(100); + await page.evaluate(() => { + // @ts-expect-error + globalThis.lynxView.reload(); + }); + await wait(100); + expect( + await page.evaluate(() => + Array.from( + document.querySelector('lynx-view')?.shadowRoot?.children || [], + ) + .filter(i => i.getAttribute('lynx-tag') === 'page').length + ), + ).toBe(1); + }); test('basic-bindtap', async ({ page }, { title }) => { await goto(page, title); await wait(100);