mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
301418a3f1
* perf(app-router): defer layout, template and boundary modules to first request The generated RSC entry statically imported every route's layout, template, loading/error/not-found/forbidden/unauthorized boundary and parallel-slot module at the top level. On a cold isolate — and on the dev server's first request — that eagerly evaluated the whole app's segment tree, including routes never visited in that lifetime. Page and route-handler modules were already lazy; the surrounding segment modules were not. Emit those modules as lazy `() => import()` loaders paired with `null` placeholders in the manifest, and hydrate them onto the route's synchronous fields in `ensureAppRouteModulesLoaded` before any read (extended from page/handler to layouts, templates, boundaries and parallel-slot modules). Intercept layouts hydrate via the dedicated `loadAppInterceptLayouts` because they live on the match object, not the route. Every reader already runs behind `ensureAppRouteModulesLoaded` (handler dispatch and `buildPageElements`) or the intercept resolvers, so no synchronous module field is read before hydration. The shared intercept `interceptLayouts` arrays keep their `readonly` public types; the loader is their sole writer and fills slots through one boundary-local cast, so route metadata stays immutable to callers. Dev cold start is unchanged on page-heavy apps — pages were already lazy — but on a boundary-heavy app (80 independent sections, each with layout+template+error+loading+not-found) first-request module compile drops 704ms -> 534ms (~24%) and time-to-first-200 1917ms -> 1665ms. The saving scales with the number of unrelated segments deferred. * test(app-router): localize react max header font fixture * fix(app-router): dedupe lazy intercept module loads * fix(app-router): share intercept load state across matches * docs(app-router): explain root layout loader reuse --------- Co-authored-by: James <james@eli.cx>