Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimization(runtime): create smaller objects for each Astro global #10773

Merged
merged 4 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/lazy-rats-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"astro": patch
---

Improves performance for frequent use of small components.
59 changes: 49 additions & 10 deletions packages/astro/src/core/render-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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() {
Expand Down