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

Refactor sidebar persistence logic for better slow device performance #2242

Merged
merged 4 commits into from
Sep 6, 2024

Conversation

delucis
Copy link
Member

@delucis delucis commented Aug 20, 2024

Description

This PR refactors how the sidebar persistence logic works (yes, already 😅).

Before this PR, we restored state for sidebar groups in a single inline script at the bottom of the sidebar. This is mostly working very well, but on underpowered devices — or, perhaps more importantly, devices momentarily under some strain — this can mean a partial render of the sidebar can be shown before the sidebar is fully rendered, and state is restored only after that. This can lead to a “flash of unrestored sidebar”. For example, here are two frames from a performance trace with 20x CPU slowdown, showing first a partial sidebar with groups expanded and then the restored state with groups collapsed:

performance trace screenshot

The refactor moves the restore logic into a small custom element. Every sidebar group (rendered by <SidebarSublist> renders this custom element, which triggers the state restore for that group. This means state can be restored incrementally as the sidebar renders instead of once after the full DOM is ready. The only exception is for the scroll position restoration. Setting scrollTop forces style recalc/layout work, so that still happens only once to minimise that. That means on a slow device, scroll restoration can still result in a visible jump in theory but it is overall still much more stable.

One detail I’m still looking at is how to generate an index for each sidebar group. Currently this is happening during rendering, but it might make more sense to move it to the sidebar generation logic directly.

Copy link

changeset-bot bot commented Aug 20, 2024

🦋 Changeset detected

Latest commit: 1bb06c3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@astrojs/starlight Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the 🌟 core Changes to Starlight’s main package label Aug 20, 2024
Copy link

netlify bot commented Aug 20, 2024

Deploy Preview for astro-starlight ready!

Name Link
🔨 Latest commit 1bb06c3
🔍 Latest deploy log https://app.netlify.com/sites/astro-starlight/deploys/66db35f82e5dc2000882bc0a
😎 Deploy Preview https://deploy-preview-2242--astro-starlight.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 100 (no change from production)
Accessibility: 100 (no change from production)
Best Practices: 100 (no change from production)
SEO: 92 (no change from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify site configuration.

@astrobot-houston
Copy link
Collaborator

astrobot-houston commented Aug 20, 2024

size-limit report 📦

Path Size
/index.html 6.15 KB (-0.02% 🔽)
/_astro/*.js 22.36 KB (-0.05% 🔽)
/_astro/*.css 13.72 KB (0%)

Copy link
Member

@HiDeoo HiDeoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really impressive refactor, tested it more and all the reproducible situations that were previously potentially showing a flash of unrestored sidebar no longer exhibit that behavior 👏

Just left a tiny question/suggestion but otherwise this is looking great to me.

packages/starlight/components/SidebarPersister.astro Outdated Show resolved Hide resolved
delucis and others added 2 commits August 21, 2024 19:17
Co-authored-by: HiDeoo <494699+HiDeoo@users.noreply.github.com>
@delucis delucis marked this pull request as ready for review September 6, 2024 16:59
@delucis
Copy link
Member Author

delucis commented Sep 6, 2024

Marked this as ready. I thought a bit more about the SidebarRestorePoint index generation and the current approach is pretty nicely contained and I think it should be reliable, so I think I’m happy with keeping it unless I hear other feedback!

@delucis delucis added the 🌟 patch Change that triggers a patch release label Sep 6, 2024
<sl-sidebar-state-persist data-hash={hash}>
<script is:inline>
(() => {
try {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the try/catch needed? I don't see anythink here that is likely to throw.

Also, you can do:

{
  const target  = ...
}

instead of the function wrapper on line 32, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/block#block_scoping_rules_with_let_const_class_or_function_declaration_in_strict_mode

Copy link
Member Author

@delucis delucis Sep 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try/catch is mainly to defend against the state loading in case sessionStorage is unavailable or for some reason it got some bad value stored to it so the JSON.parse() throws.

Good point about block scoping! Hadn’t considered that before for these inline scripts. I wonder if they’re a little too implicit in what they’re doing? Ultimately both those and the IIFE are not super explicit about locking down scope, but the IIFE feels a little more obvious to me. The {} wrapping almost looks like a mistake if you’re not familiar.

(In the past I’ve only ever really used those in tests, where I wanted to repeatedly declare some variable and test it, so the scoping was much more obvious from the repetition.)

---
/** Unique symbol for storing a running index in `locals`. */
const currentGroupIndexSymbol = Symbol.for('starlight-sidebar-group-index');
const locals = Astro.locals as Record<typeof currentGroupIndexSymbol, number>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sneaky, i like it.

@delucis delucis merged commit 756e85e into main Sep 6, 2024
15 checks passed
@delucis delucis deleted the chris/sidebar-perf branch September 6, 2024 22:18
@astrobot-houston astrobot-houston mentioned this pull request Sep 6, 2024
HiDeoo added a commit to HiDeoo/starlight that referenced this pull request Sep 7, 2024
* main: (37 commits)
  [ci] format
  i18n(ko-KR): update `manual-setup.mdx` (withastro#2294)
  i18n(ko-KR): update `configuration.mdx` (withastro#2295)
  [ci] release (withastro#2292)
  Add support for SSR (withastro#1255)
  Add Markdoc preset and example (withastro#2249)
  Refactor sidebar persistence logic for better slow device performance (withastro#2242)
  [ci] format
  Add docs.ryzekit.com to showcase (withastro#2291)
  Update astro dependency to 4.15.3 across monorepo (withastro#2289)
  [ci] release (withastro#2290)
  Prevent Zod errors from crashing build (withastro#2288)
  i18n(fr): update `guides/css-and-tailwind` (withastro#2286)
  i18n(ko-KR): update `css-and-tailwind.mdx` (withastro#2284)
  Add WCAG AAA colour contrast option to theme editor (withastro#2282)
  [ci] release (withastro#2283)
  Parse `<StarlightPage />` frontmatter asynchronously (withastro#2279)
  Ensure unhandled directives are restored without any extra whitespace (withastro#2281)
  i18n(fr): update `resources/plugins` (withastro#2278)
  i18n(ko-KR): update `plugins.mdx` (withastro#2277)
  ...
HiDeoo added a commit to HiDeoo/starlight that referenced this pull request Sep 9, 2024
* main: (22 commits)
  i18n(ru): update `ru/manual-setup.mdx` and `ru/reference/configuration.mdx` (withastro#2307)
  [ci] format
  i18n(ru): update some guides (withastro#2306)
  i18n(fr): update `manual-setup` (withastro#2299)
  i18n(fr): update `guides/pages` (withastro#2298)
  [ci] release (withastro#2304)
  Convert URL to file path correctly for Git virtual module (withastro#2303)
  i18n(fr): update `reference/configuration` (withastro#2296)
  i18n(fr): update `guides/authoring-content` (withastro#2297)
  Update `yummacss.com.png` thumbnail (withastro#2301)
  i18n(ko-KR): update `pages.mdx` (withastro#2293)
  [ci] format
  i18n(ko-KR): update `authoring-content.mdx` (withastro#2300)
  [ci] format
  i18n(ko-KR): update `manual-setup.mdx` (withastro#2294)
  i18n(ko-KR): update `configuration.mdx` (withastro#2295)
  [ci] release (withastro#2292)
  Add support for SSR (withastro#1255)
  Add Markdoc preset and example (withastro#2249)
  Refactor sidebar persistence logic for better slow device performance (withastro#2242)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🌟 core Changes to Starlight’s main package 🌟 patch Change that triggers a patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants