⚗️ Add soft navigation LCP tracking for route_change views - #4966
⚗️ Add soft navigation LCP tracking for route_change views#4966mormubis wants to merge 15 commits into
Conversation
…kspace app The nuxt-vue-router-v4-app is a generated app (gitignored, not registered in the root Yarn workspace). Playwright's webServer config starts every listed server before any test run, regardless of -g filtering. When Playwright spawned `yarn dev` in that directory, Yarn 4 traversed up to the workspace root and failed with "Package for nuxt-vue-router-v4-app@workspace:. not found" because the package isn't registered there. Switch to `yarn start` (nuxt preview of the pre-built .output/) which runs the built app directly via node and does not trigger Yarn workspace resolution.
…ntries, tighten E2E assertion, fix spec drift
| export interface RumInteractionContentfulPaintTiming { | ||
| entryType: RumPerformanceEntryType.INTERACTION_CONTENTFUL_PAINT | ||
| interactionId: number | ||
| largestContentfulPaint: RumLargestContentfulPaintTiming |
There was a problem hiding this comment.
Reused RumLargestContentfulPaintTiming here instead of a flat shape. The WICG spec types InteractionContentfulPaint.largestContentfulPaint as the full LargestContentfulPaint interface, not a subset, so this felt more accurate. Also gets toJSON() for free this way.
| } | ||
| biggestIcpSize = entry.largestContentfulPaint.size | ||
| const lcpEntry = entry.largestContentfulPaint | ||
| const largestContentfulPaint: LargestContentfulPaint = { |
There was a problem hiding this comment.
No subParts here on purpose. That breakdown (loadDelay/loadTime/renderDelay) needs a TTFB baseline from the hard nav, which doesn't really apply to a soft navigation. Left it undefined for now. Would it make more sense to have a soft-nav-specific breakdown eventually, or is undefined fine long term?
There was a problem hiding this comment.
While I admit that without a TTFB baseline (which is.... tricky to define for soft navs), loadDelay gets merged with the actual TTFB, I think there's still value in knowing if it takes a long time to be discovered (which as I say includes TTFB), load or render. So we included subparts in web-vitals library for soft navs, just with assuming TTFB is 0.
The other argument is that, for an SPA, it's easier to reduce any LCP resource loadDelay (with prefetching ether in advance, on hover, or as part of click ) so TTFB is less relevant. While it's true the LCP resource may not be known until any route fetch completes, that's still a "loadDelay" that could be worked around if the improvements made this worthwhile.
Bundles Sizes Evolution
|
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 99086e2 | Docs | View more details | Give us feedback! |
…soft-nav LCP test - Ran 'yarn format' on 3 files that were failing the format check (never ran yarn format locally during development, only yarn lint/typecheck). - The 'reports LCP' E2E test only skipped on non-chromium browsers, but chromium-pinned (Chrome 120) normalizes to browserName 'chromium' too and predates the Soft Navigation API (needs Chrome 151+). Switched to test.info().project.name for an exact match, following the same pattern already used in salesforce.scenario.ts.
- Only buffer pending ICP entries until the soft-navigation entry is known; once resolved, applyIcpEntries handles updates live and the buffer was never read again, so it was just accumulating for no reason. - Fix chromium-pinned being silently skipped in the 'does not error on browsers without the soft navigation API' test, same browserName vs project.name normalization issue already fixed elsewhere. It's a real Chromium build without the API, a better fit for this test than relying on firefox/webkit alone.
|
Super excited to see this! Let me know if you have any questions or anything I can do to help. |
Motivation
route_changeviews never report LCP today.trackInitialViewMetricsonly runs forinitial_load, so SPAs get zero LCP after the first page. Chrome's Soft Navigation API (stable since Chrome 151, no flag needed on their end) actually solves this now:soft-navigation+interaction-contentful-paintentries let us compute LCP per client-side navigation.There's an open GitHub issue for this (#2696) and an earlier draft PR (#4154) that only added a boolean
is_soft_navigationflag without touching LCP, which isn't really what the issue is asking for. This PR does the actual LCP computation.Changes
New
ExperimentalFeature.SOFT_NAVIGATIONflag. When enabled and the browser supports it,route_changeviews get atrackRouteChangeViewMetricstracker (same shape astrackInitialViewMetrics) that subscribes tosoft-navigationandinteraction-contentful-paintentries, correlates them byinteractionId, and fills inview.performance.lcp.*. No schema changes, reuses the existing field.The tricky part was figuring out when to stop listening for the
soft-navigationentry. It's per-view and Chrome fires it async, so a view whose interaction never actually produced a soft navigation would keep listening and could steal the next view's entry if I didn't unsubscribe it in time. Left a comment on that.Test instructions
enableExperimentalFeatures: ['soft_navigation']in your init config.history.pushStatein the same task (that's the heuristic Chrome uses to detect a soft navigation).route_changeview event,view.performance.lcp.timestampshould be populated.undefinedlike before.Unit tests:
yarn test:unit --spec packages/browser-rum-core/src/domain/view/viewMetrics/trackRouteChangeViewMetrics.spec.tsandyarn test:unit --spec packages/browser-rum-core/src/domain/view/trackViews.spec.tsE2E:
yarn test:e2e -g "soft navigation"(Chromium only). Confirmed green in CI across all browser variants.Checklist