From 40f464c8e75842cdbff0177a286f87bc3b04eef2 Mon Sep 17 00:00:00 2001 From: Jasper Moelker Date: Fri, 3 Jan 2025 16:56:46 +0100 Subject: [PATCH 1/2] docs: performance (wip) --- README.md | 33 +++++++++++++++++---------------- docs/performance.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 docs/performance.md diff --git a/README.md b/README.md index 60ab13d..3023b16 100644 --- a/README.md +++ b/README.md @@ -64,25 +64,26 @@ flowchart LR ## Getting started -Follow the step-by-step [getting started instructions](docs/getting-started.md). +Follow the step-by-step [getting started instructions](./docs/getting-started.md). ## Documentation -All documentation is located in [`docs/`](docs/): - -- [Getting Started](docs/getting-started.md) -- [Accessibility (a11y)](docs/accessibility.md) -- [Blocks and Components](docs/blocks-and-components.md) -- [CMS Content Modelling](docs/cms-content-modelling.md) -- [CMS Data Loading](docs/cms-data-loading.md) -- [Internationalization (i18n)](docs/i18n.md) -- [Project Structure](docs/project-structure.md) -- [Preview Mode](docs/preview-mode.md) -- [Routing](docs/routing.md) -- [Search](docs/search.md) -- [Search Engine Optimisation (SEO)](docs/seo.md) -- [Testing](docs/testing.md) -- [Upgrading](docs/upgrading.md) +All documentation is located in [`./docs/`](docs/): + +- [Getting Started](./docs/getting-started.md) +- [Accessibility (a11y)](./docs/accessibility.md) +- [Blocks and Components](./docs/blocks-and-components.md) +- [CMS Content Modelling](./docs/cms-content-modelling.md) +- [CMS Data Loading](./docs/cms-data-loading.md) +- [Internationalization (i18n)](./docs/i18n.md) +- [Performance](./docs/performance.md) +- [Project Structure](./docs/project-structure.md) +- [Preview Mode](./docs/preview-mode.md) +- [Routing](./docs/routing.md) +- [Search](./docs/search.md) +- [Search Engine Optimisation (SEO)](./docs/seo.md) +- [Testing](./docs/testing.md) +- [Upgrading](./docs/upgrading.md) ## Commands diff --git a/docs/performance.md b/docs/performance.md new file mode 100644 index 0000000..cf21645 --- /dev/null +++ b/docs/performance.md @@ -0,0 +1,29 @@ +# Performance + +**Head Start is built with performance-minded technology and introduces additional performance patterns. In addition we have some tips to further tune the performance of your specific project.** + +## ... + +Why Astro is selected, zero JS by default +Pre rendering with Cloudflare +Runtime rendering on Cloudflare edge workers +DatoCMS image service features +Use of facade patterns for video embeds +Assets caching (Cache static assets #197) +Pre-connect origins (Pre-connect to DatoCMS Assets domain #200) +Service worker (Add Service Worker #199) +Astro prefetch (Configure Astro prefetch behaviour #201) +Runtime caching (Cache runtime routes (ISR) #198) +Advise 3rd party scripts in workers using Partytown (Plausible example) + +link PerfHead? + +## Tip: add a service worker + +## Tip: add Astro prefetch + +## Tip: cache runtime routes + +## Tip: load 3rd party scripts in workers + +Most 3rd party modules are installed via npm and bundled as our own application code. We have fine grained control over the version of each module, when, how and what part we import. However most projects will inevitably need to load 3rd party scripts (like analytics) during runtime from 3rd party domains. For those 3rd party scripts we advice to use Partytown. From 5854e02cdaf84cf9c7d650dac774907e9d302aaf Mon Sep 17 00:00:00 2001 From: Jasper Moelker Date: Sun, 12 Jan 2025 12:20:21 +0100 Subject: [PATCH 2/2] docs: performance optimisations and tips --- README.md | 1 + docs/performance.md | 77 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index aee329b..34a1604 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ All documentation is located in [`./docs/`](docs/): - [CMS Content Modelling](./docs/cms-content-modelling.md) - [CMS Data Loading](./docs/cms-data-loading.md) - [Internationalization (i18n)](./docs/i18n.md) +- [Performance](./docs/performance.md) - [Project Structure](./docs/project-structure.md) - [Preview Mode](./docs/preview-mode.md) - [Routing](./docs/routing.md) diff --git a/docs/performance.md b/docs/performance.md index cf21645..9bc8fdf 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -2,28 +2,75 @@ **Head Start is built with performance-minded technology and introduces additional performance patterns. In addition we have some tips to further tune the performance of your specific project.** -## ... +## Zero JS by default -Why Astro is selected, zero JS by default -Pre rendering with Cloudflare -Runtime rendering on Cloudflare edge workers -DatoCMS image service features -Use of facade patterns for video embeds -Assets caching (Cache static assets #197) -Pre-connect origins (Pre-connect to DatoCMS Assets domain #200) -Service worker (Add Service Worker #199) -Astro prefetch (Configure Astro prefetch behaviour #201) -Runtime caching (Cache runtime routes (ISR) #198) -Advise 3rd party scripts in workers using Partytown (Plausible example) +Head Start has selected Astro as its web framework, for one reason Astro being zero JS by default. This provides a much better performance baseline than the popular isomorphic meta frameworks out there. Head Start embraces the Zero JS by default and only adds improved interactivity through progressive enhancement using standalone Web Components. You are free to add a JS framework when using Head Start for your project. Astro plays well with all of them. We advice to use a performance-minded framework like Preact or Svelte when possible. -link PerfHead? -## Tip: add a service worker +## Pre and Edge rendering + +Head Start is configured to pre-render all routes by default (using [Astro's `output: 'static'`](https://docs.astro.build/en/reference/configuration-reference/#output)). This means every route is cached as a static file by default. Which is great for performance. Routes that require [on-demand rendering](https://docs.astro.build/en/guides/on-demand-rendering/), opt-out of pre-rendering using `export const prerender = false`. These routes are then executed runtime on Cloudflare edge workers. These are faster than traditional serverless functions with a Node.js runtime. While a bit less powerful these Cloudflare Workers are great for performance. If you use on-demand rendering we advice you to [configure cache for runtime routes](#tip-cache-runtime-routes) for more performance. + + +## DatoCMS image service + +Head Start uses DatoCMS which comes with its own [image service](https://www.datocms.com/docs/asset-api/images) powered by Imgix, a super-fast CDN optimized for image delivery, which provides on-the-fly image manipulations and caching. Image transformations can be configured entirely through the image URL. By requesting the right size, format, quality and compression the performance can be greatly improved. + + +## Facade patterns + +Head Start uses the facade pattern for embeds like videos. This means a placeholder is displayed on load, and the code required to run the embed is only loaded on intent. This saves on resources and improves performance of the initial page rendering. + + +## Assets caching + +Head Start is configured to serve all generated [assets](./assets.md) (in `/_astro/`) with immutable caching headers (see `public/_headers`). This means subsequent requests can always use the browser's network cache, which significantly speeds up page rendering. + + +## Resource hints + +Head Start uses resource hints to improve perceived page speed. `link[rel=preconnect]` is used to establish early network connections to 3rd party domains (like the DatoCMS assets and graphql subdomains). `link[rel=preload]` is used to load critical resources (like custom fonts) early. These are all configured in [`layouts/PerfHead`](../src/layouts/PerfHead/README.md). You can extend this with your own resources. + + +## Service Worker + +Head Start adds a basic Service Worker with a network-first strategy for pages and a cache-first strategy for assets. This ensures visited pages always load within a few seconds, even when the server can't be reached. The Service Worker setup acts as a foundation for future performance improvements. -## Tip: add Astro prefetch ## Tip: cache runtime routes +Head Start mostly contains statically generated pages, which are effectively cached as HTML during build-time. Routes with `export const prerender = false` are not cached by default. To improve performance and reduce the load on DatoCMS and possible other services, we advice to cache runtime routes. + +For example, cache a route for 60 seconds, on the shared CDN cache, using `s-max-age`, in an Astro page route (`pages/**/*.astro`): + +```astro +--- +// Set Cache-Control headers for CDN caching +Astro.response.headers.set('Cache-Control', 'public, s-maxage=60'); +--- +``` + +Or in an API route (`api/**/*.ts`): + +```ts +export const GET: APIRoute = () => { + return new Response('...', { + // Set Cache-Control headers for CDN caching + headers: { 'Cache-Control': 'public, s-maxage=60' }, + }); +}; +``` + +Note: this may be integrated into Head Start in the future. See [#198: Cache runtime routes (ISR)](https://github.com/voorhoede/head-start/issues/198). + + +## Tip: add Astro prefetch + +[Astro provides a configurable prefetch behaviour](https://docs.astro.build/en/guides/prefetch/) to improve performance of loading the next page. The prefetching is speculative and trades extra requests and server load for a better user experience. The behaviour doesn't fully work in every browser as Cloudflare does not provide an etag header on pages. While we see the benefits of the Astro prefetch behaviour, we haven't set a default. We advice you to try the feature in your project and configure a setting that works for you. + +Note: this may be integrated into Head Start in the future. See [#201: Configure Astro prefetch behaviour](https://github.com/voorhoede/head-start/issues/201). + + ## Tip: load 3rd party scripts in workers Most 3rd party modules are installed via npm and bundled as our own application code. We have fine grained control over the version of each module, when, how and what part we import. However most projects will inevitably need to load 3rd party scripts (like analytics) during runtime from 3rd party domains. For those 3rd party scripts we advice to use Partytown.