Skip to content

Support Lazy Code Splits #16

@TomStrepsil

Description

@TomStrepsil

Issue

All variants of all features are currently part of the main bundle. This means the code is adding to the download and execution profile for all traffic, despite some requests not utilising any variation.

It would good to split chunks for toggles. For simplicity, perhaps at the "feature" rather than the "variant" level, for experiment toggles. All code for an experiment (all buckets) should be in a single bundle, lazy loaded once the first component is hit that requires the experiment.

Details

All experiments are bundled together with the main codebase, but experimental code ideally wouldn't affect the performance of the main codebase, so should be a separate bundle that needn't execute for the critical / entrypoint bundle to bootstrap, and perhaps even optionally downloaded if not needed for the critical render path.

Lazy splits have been successfully POC'd, but support for lazy splits / suspense on SSR was not in a good place with React 16/17.

Now that React 18 supports Suspense on the server, the primary idea to resolve would be utilising a null fallback to a Suspense, to prevent flash-of-control-content. POC: https://github.com/jlp-craigmorten/react-hydrate-lazy-component, inspired by facebook/react#19715.

As per documentation:

When a component suspends, the closest parent Suspense boundary switches to showing the fallback. This can lead to a jarring user experience if it was already displaying some content.
...
To prevent this, you can mark the navigation state update as a Transition with startTransition:
...
This tells React that the state transition is not urgent, and it’s better to keep showing the previous page instead of hiding any already revealed content.

Perhaps, in combination with useTransition? Suggestion here: https://dev.to/tmns/uselazyload-code-splitting-in-react-without-suspense-4e2o

Or, perhaps using useDeferredValue?

Other Ideas:

  • There may be a way to support lazy splits on the client, but non-lazy splits on the server - or find a way to use concurrent features of React 18 to support lazy splits on the server too. However, ASOS Product Listing Page is using "preact" and its unclear if that even supports React 18 in compatibility mode.

  • Could consider a completely different approach on the server to the client, but done in an "isomorphic / universal" fashion to the consumer.

  • Perhaps using something like https://github.com/Kennypt/react-lazy-hydrate or https://github.com/Tinkoff/react-lazy-hydration-render could be used to avoid hydrating the experimental part of the DOM until the lazy loaded chunk is available (which should be "next tick" instantaneous if the lazy chunks are written as script tags to the DOM prior to the entry point)

  • Consider: https://github.com/faceyspacey/react-universal-component/ or just utilising resolveWeak added here

    Via a simple API it gives you the chunks (javascript, stylesheets, etc) corresponding to the modules that were synchronously rendered on the server, which without this package would be asynchronously rendered on the client. In doing so, it also allows your first client-side render on page-load to render async components synchronously!

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions