From 83016795e9e149bc64e2441d477cf8c65ef5a117 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 27 Jun 2023 21:16:10 +0800 Subject: [PATCH] Simplify nested hydration flow (#7370) --- .changeset/gorgeous-tables-sparkle.md | 5 ++++ .github/scripts/bundle-size.mjs | 4 ++- packages/astro/src/runtime/client/events.ts | 20 ------------- .../runtime/client/hydration-directives.d.ts | 17 ----------- .../astro/src/runtime/server/astro-island.ts | 29 ++++++++++--------- 5 files changed, 24 insertions(+), 51 deletions(-) create mode 100644 .changeset/gorgeous-tables-sparkle.md delete mode 100644 packages/astro/src/runtime/client/events.ts delete mode 100644 packages/astro/src/runtime/client/hydration-directives.d.ts diff --git a/.changeset/gorgeous-tables-sparkle.md b/.changeset/gorgeous-tables-sparkle.md new file mode 100644 index 000000000000..0613fac562ec --- /dev/null +++ b/.changeset/gorgeous-tables-sparkle.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Simplify nested hydration flow diff --git a/.github/scripts/bundle-size.mjs b/.github/scripts/bundle-size.mjs index 618d6e8cb546..49c3c5bedb07 100644 --- a/.github/scripts/bundle-size.mjs +++ b/.github/scripts/bundle-size.mjs @@ -23,7 +23,9 @@ export default async function checkBundleSize({ github, context }) { ...context.repo, pull_number: PR_NUM, }); - const clientRuntimeFiles = files.filter(({ filename }) => filename.startsWith(CLIENT_RUNTIME_PATH)); + const clientRuntimeFiles = files.filter((file) => { + return file.filename.startsWith(CLIENT_RUNTIME_PATH) && file.status !== 'removed' + }); if (clientRuntimeFiles.length === 0) return; const table = [ diff --git a/packages/astro/src/runtime/client/events.ts b/packages/astro/src/runtime/client/events.ts deleted file mode 100644 index 93a8c2600a21..000000000000 --- a/packages/astro/src/runtime/client/events.ts +++ /dev/null @@ -1,20 +0,0 @@ -const HYDRATE_KEY = `astro:hydrate`; -function debounce any>(cb: T, wait = 20) { - let h = 0; - let callable = (...args: any) => { - clearTimeout(h); - h = setTimeout(() => cb(...args), wait) as unknown as number; - }; - return callable as T; -} - -export const notify = debounce(() => { - window.dispatchEvent(new CustomEvent(HYDRATE_KEY)); -}); - -if (!(window as any)[HYDRATE_KEY]) { - if ('MutationObserver' in window) { - new MutationObserver(notify).observe(document.body, { subtree: true, childList: true }); - } - (window as any)[HYDRATE_KEY] = true; -} diff --git a/packages/astro/src/runtime/client/hydration-directives.d.ts b/packages/astro/src/runtime/client/hydration-directives.d.ts deleted file mode 100644 index 6832ab24fd43..000000000000 --- a/packages/astro/src/runtime/client/hydration-directives.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { GetHydrateCallback, HydrateOptions } from '../../@types/astro'; - -type DirectiveLoader = (get: GetHydrateCallback, opts: HydrateOptions, root: HTMLElement) => void; - -declare global { - interface Window { - Astro: { - idle: DirectiveLoader; - load: DirectiveLoader; - media: DirectiveLoader; - only: DirectiveLoader; - visible: DirectiveLoader; - }; - } -} - -export {}; diff --git a/packages/astro/src/runtime/server/astro-island.ts b/packages/astro/src/runtime/server/astro-island.ts index b24e6c0be609..3f93cd817500 100644 --- a/packages/astro/src/runtime/server/astro-island.ts +++ b/packages/astro/src/runtime/server/astro-island.ts @@ -59,7 +59,6 @@ declare const Astro: { } } async childrenConnectedCallback() { - window.addEventListener('astro:hydrate', this.hydrate); let beforeHydrationUrl = this.getAttribute('before-hydration-url'); if (beforeHydrationUrl) { await import(beforeHydrationUrl); @@ -96,17 +95,22 @@ declare const Astro: { this ); } - hydrate = () => { - if ( - !this.hydrator || - // Make sure the island is mounted on the DOM before hydrating. It could be unmounted - // when the parent island hydrates and re-creates this island. - !this.isConnected || - // Wait for parent island to hydrate first so we hydrate top-down. - this.parentElement?.closest('astro-island[ssr]') - ) { + hydrate = async () => { + // The client directive needs to load the hydrator code before it can hydrate + if (!this.hydrator) return; + + // Make sure the island is mounted on the DOM before hydrating. It could be unmounted + // when the parent island hydrates and re-creates this island. + if (!this.isConnected) return; + + // Wait for parent island to hydrate first so we hydrate top-down. The `ssr` attribute + // represents that it has not completed hydration yet. + const parentSsrIsland = this.parentElement?.closest('astro-island[ssr]'); + if (parentSsrIsland) { + parentSsrIsland.addEventListener('astro:hydrate', this.hydrate, { once: true }); return; } + const slotted = this.querySelectorAll('astro-slot'); const slots: Record = {}; // Always check to see if there are templates. @@ -126,12 +130,11 @@ declare const Astro: { const props = this.hasAttribute('props') ? JSON.parse(this.getAttribute('props')!, reviver) : {}; - this.hydrator(this)(this.Component, props, slots, { + await this.hydrator(this)(this.Component, props, slots, { client: this.getAttribute('client'), }); this.removeAttribute('ssr'); - window.removeEventListener('astro:hydrate', this.hydrate); - window.dispatchEvent(new CustomEvent('astro:hydrate')); + this.dispatchEvent(new CustomEvent('astro:hydrate')); }; attributeChangedCallback() { this.hydrate();