summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/lazy-rats-beam.md5
-rw-r--r--packages/astro/src/core/render-context.ts59
2 files changed, 54 insertions, 10 deletions
diff --git a/.changeset/lazy-rats-beam.md b/.changeset/lazy-rats-beam.md
new file mode 100644
index 000000000..66acfe33f
--- /dev/null
+++ b/.changeset/lazy-rats-beam.md
@@ -0,0 +1,5 @@
+---
+"astro": patch
+---
+
+Improves performance for frequent use of small components.
diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts
index eb05df6f5..5cfc8ef2e 100644
--- a/packages/astro/src/core/render-context.ts
+++ b/packages/astro/src/core/render-context.ts
@@ -242,12 +242,57 @@ export class RenderContext {
return result;
}
+ #astroPagePartial?: Omit<AstroGlobal, 'props' | 'self' | 'slots'>;
+ /**
+ * The Astro global is sourced in 3 different phases:
+ * - **Static**: `.generator` and `.glob` is printed by the compiler, instantiated once per process per astro file
+ * - **Page-level**: `.request`, `.cookies`, `.locals` etc. These remain the same for the duration of the request.
+ * - **Component-level**: `.props`, `.slots`, and `.self` are unique to each _use_ of each component.
+ *
+ * The page level partial is used as the prototype of the user-visible `Astro` global object, which is instantiated once per use of a component.
+ */
createAstro(
result: SSRResult,
- astroGlobalPartial: AstroGlobalPartial,
+ astroStaticPartial: AstroGlobalPartial,
props: Record<string, any>,
slotValues: Record<string, any> | null
): AstroGlobal {
+ // Create page partial with static partial so they can be cached together.
+ const astroPagePartial = (this.#astroPagePartial ??= this.createAstroPagePartial(
+ result,
+ astroStaticPartial
+ ));
+ // Create component-level partials. `Astro.self` is added by the compiler.
+ const astroComponentPartial = { props, self: null };
+
+ // Create final object. `Astro.slots` will be lazily created.
+ const Astro: Omit<AstroGlobal, 'self' | 'slots'> = Object.assign(
+ Object.create(astroPagePartial),
+ astroComponentPartial
+ );
+
+ // Handle `Astro.slots`
+ let _slots: AstroGlobal['slots'];
+ Object.defineProperty(Astro, 'slots', {
+ get: () => {
+ if (!_slots) {
+ _slots = new Slots(
+ result,
+ slotValues,
+ this.pipeline.logger
+ ) as unknown as AstroGlobal['slots'];
+ }
+ return _slots;
+ },
+ });
+
+ return Astro as AstroGlobal;
+ }
+
+ createAstroPagePartial(
+ result: SSRResult,
+ astroStaticPartial: AstroGlobalPartial
+ ): Omit<AstroGlobal, 'props' | 'self' | 'slots'> {
const renderContext = this;
const { cookies, locals, params, pipeline, request, url } = this;
const { response } = result;
@@ -260,12 +305,10 @@ export class RenderContext {
}
return new Response(null, { status, headers: { Location: path } });
};
- const slots = new Slots(result, slotValues, pipeline.logger) as unknown as AstroGlobal['slots'];
- // `Astro.self` is added by the compiler
- const astroGlobalCombined: Omit<AstroGlobal, 'self'> = {
- generator: astroGlobalPartial.generator,
- glob: astroGlobalPartial.glob,
+ return {
+ generator: astroStaticPartial.generator,
+ glob: astroStaticPartial.glob,
cookies,
get clientAddress() {
return renderContext.clientAddress();
@@ -280,17 +323,13 @@ export class RenderContext {
get preferredLocaleList() {
return renderContext.computePreferredLocaleList();
},
- props,
locals,
redirect,
request,
response,
- slots,
site: pipeline.site,
url,
};
-
- return astroGlobalCombined as AstroGlobal;
}
clientAddress() {