Skip to content

Commit 35e43ec

Browse files
lilnasybluwy
andauthored
optimization(runtime): create smaller objects for each Astro global (#10773)
* optimization(runtime): create smaller objects for each Astro global * add changeset * Make slots lazy --------- Co-authored-by: bluwy <bjornlu.dev@gmail.com>
1 parent 5750ad1 commit 35e43ec

2 files changed

Lines changed: 54 additions & 10 deletions

File tree

.changeset/lazy-rats-beam.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"astro": patch
3+
---
4+
5+
Improves performance for frequent use of small components.

packages/astro/src/core/render-context.ts

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,57 @@ export class RenderContext {
242242
return result;
243243
}
244244

245+
#astroPagePartial?: Omit<AstroGlobal, 'props' | 'self' | 'slots'>;
246+
/**
247+
* The Astro global is sourced in 3 different phases:
248+
* - **Static**: `.generator` and `.glob` is printed by the compiler, instantiated once per process per astro file
249+
* - **Page-level**: `.request`, `.cookies`, `.locals` etc. These remain the same for the duration of the request.
250+
* - **Component-level**: `.props`, `.slots`, and `.self` are unique to each _use_ of each component.
251+
*
252+
* 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.
253+
*/
245254
createAstro(
246255
result: SSRResult,
247-
astroGlobalPartial: AstroGlobalPartial,
256+
astroStaticPartial: AstroGlobalPartial,
248257
props: Record<string, any>,
249258
slotValues: Record<string, any> | null
250259
): AstroGlobal {
260+
// Create page partial with static partial so they can be cached together.
261+
const astroPagePartial = (this.#astroPagePartial ??= this.createAstroPagePartial(
262+
result,
263+
astroStaticPartial
264+
));
265+
// Create component-level partials. `Astro.self` is added by the compiler.
266+
const astroComponentPartial = { props, self: null };
267+
268+
// Create final object. `Astro.slots` will be lazily created.
269+
const Astro: Omit<AstroGlobal, 'self' | 'slots'> = Object.assign(
270+
Object.create(astroPagePartial),
271+
astroComponentPartial
272+
);
273+
274+
// Handle `Astro.slots`
275+
let _slots: AstroGlobal['slots'];
276+
Object.defineProperty(Astro, 'slots', {
277+
get: () => {
278+
if (!_slots) {
279+
_slots = new Slots(
280+
result,
281+
slotValues,
282+
this.pipeline.logger
283+
) as unknown as AstroGlobal['slots'];
284+
}
285+
return _slots;
286+
},
287+
});
288+
289+
return Astro as AstroGlobal;
290+
}
291+
292+
createAstroPagePartial(
293+
result: SSRResult,
294+
astroStaticPartial: AstroGlobalPartial
295+
): Omit<AstroGlobal, 'props' | 'self' | 'slots'> {
251296
const renderContext = this;
252297
const { cookies, locals, params, pipeline, request, url } = this;
253298
const { response } = result;
@@ -260,12 +305,10 @@ export class RenderContext {
260305
}
261306
return new Response(null, { status, headers: { Location: path } });
262307
};
263-
const slots = new Slots(result, slotValues, pipeline.logger) as unknown as AstroGlobal['slots'];
264308

265-
// `Astro.self` is added by the compiler
266-
const astroGlobalCombined: Omit<AstroGlobal, 'self'> = {
267-
generator: astroGlobalPartial.generator,
268-
glob: astroGlobalPartial.glob,
309+
return {
310+
generator: astroStaticPartial.generator,
311+
glob: astroStaticPartial.glob,
269312
cookies,
270313
get clientAddress() {
271314
return renderContext.clientAddress();
@@ -280,17 +323,13 @@ export class RenderContext {
280323
get preferredLocaleList() {
281324
return renderContext.computePreferredLocaleList();
282325
},
283-
props,
284326
locals,
285327
redirect,
286328
request,
287329
response,
288-
slots,
289330
site: pipeline.site,
290331
url,
291332
};
292-
293-
return astroGlobalCombined as AstroGlobal;
294333
}
295334

296335
clientAddress() {

0 commit comments

Comments
 (0)