-
[OPIK-6066] [FE] perf: improve first load time (cache + v2 default + lazy pages) (#6429)
发布于
2026-04-22 11:40:16 +00:00 - [OPIK-6066] [FE] perf: cache workspace version per workspace, default to v2
Post-signup users landing on
/(no workspace in URL) used to optimistically
load V1App, then hard-reload into V2App once WorkspaceVersionResolver fetched
the real version from the API — paying the cost twice.This change adds a per-workspace
opik-workspace-versionslocalStorage map
populated whenever the API resolves a version (in WorkspaceVersionGate's
async path and in WorkspaceVersionResolver's React Query effect). The sync
resolver now consults the cache before falling back to a Loader, so
returning users see the correct App on first paint with no API round-trip.The optimistic default for the no-workspace case is also flipped from v1 to
v2, matching the direction of the product. The existing reload-on-mismatch
self-correction is intentionally left in place to handle stale-cache cases
(e.g. a workspace migrating from v1 to v2) — the reload writes the corrected
version to the cache before reloading, so the next visit loads the right
App synchronously.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- [OPIK-6066] [FE] perf: swap v1/v2 App in place instead of reloading
WorkspaceVersionResolver was calling window.location.reload() when the
gate's optimistic version disagreed with the API — throwing away React
state and re-running the entire bootstrap. Since the Resolver already
updates the store (which the gate subscribes to via useWorkspaceVersion),
removing the reload lets React.lazy + Suspense swap V1App ↔ V2App in
place.Worst-case version-mismatch cost drops from "full page reload + JS
reparse + auth waterfall re-run" to "lazy chunk load + subtree remount".
Cache write stays on every API resolution so the gate's sync path is
correct on the next visit.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- [OPIK-6066] [FE] fix: restore reload on version mismatch to avoid router URL desync
The in-place V1App↔V2App swap introduced in the previous commit caused an
infinite version-swap loop when users switched between workspaces of
different versions.Root cause: each App module creates its own TanStack Router instance at
module scope (src/v1/router.tsx, src/v2/router.tsx). These routers have
internal URL state that does not auto-sync when unmounts
and later remounts. When V2App navigated to a new URL and then V1App
mounted, V1's router still reported the previous URL. Its
WorkspacePreloader consequently set activeWorkspaceName to the stale
workspace, the Resolver fetched the wrong version, and the gate kept
swapping apps indefinitely.Restoring window.location.reload() on mismatch resolves the loop by
re-evaluating every module against the current browser URL, so both
routers start from a consistent state. The Tier 0 cache + #3.5 gate
simplification remain in place; reload path stays until a proper
cross-App router-sync fix ships.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- [OPIK-6066] [FE] perf: lazy-load heaviest non-critical page routes
Converts the heaviest page imports in v1/router.tsx and v2/router.tsx
from static imports to React.lazy() so each page becomes its own chunk,
loaded only on navigation. Follows the SMEFlowPage precedent in both
routers.Pages converted:
- v1: PlaygroundPage, PromptPage, CompareExperimentsPage, AlertsRouteWrapper
- v2: PlaygroundPage, CompareExperimentsPage, AlertsRouteWrapper
TracesPage intentionally kept eager — it's on the critical path and
lazy-loading would cause a visible Loader flash on most sessions.Measured impact (production build, dist/assets):
- V1App chunk: 932 KB -> 801 KB (-131 KB, -14%)
- V2App chunk: 1074 KB -> 955 KB (-119 KB, -11%)
- Combined initial App payload: -250 KB
- Total JS across all chunks: +52 KB (+0.4%) — Rollup duplicates some
small shared modules across chunks; acceptable tax for the initial
payload reduction.
App-level <Suspense fallback={}> in WorkspaceVersionGate
covers the fallback for all new lazy route components — no new
Suspense boundaries needed.Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
- [OPIK-6066] [FE] fix: verify workspace version on pair routes
Pair routes (/pair/v1 and the /opik/pair/v1 OSS alias) render outside
WorkspaceGuard — they must work without auth — so WorkspaceVersionResolver
was never mounted for them. Combined with OPIK-6066's optimistic v2
default, a v1-workspace user clicking an SDK-generated pair link with a
cold cache would have landed on V2App's PairingPage instead of V1App's
"upgrade required" screen.Adds PairRouteVersionGuard which seeds activeWorkspaceName from the
pair URL's ?workspace= query and mounts WorkspaceVersionResolver. The
Resolver's existing API check + reload-on-mismatch flow now runs for
pair routes too:- v2 workspace + cache miss: V2 PairingPage renders immediately,
Resolver confirms v2 in background, no reload. - v1 workspace + cache miss: gate picks v2 optimistically → Resolver
detects v1 → reload → V1 PairV1Page (upgrade-required) renders. - cache hit: correct App on first paint, Resolver confirms.
Caught by baz-reviewer[bot] on PR #6429.
Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com
Co-authored-by: Claude Opus 4.7 (1M context) noreply@anthropic.com
下载附件