[go: up one dir, main page]

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

Add support for reporting metrics on bfcache restore #87

Merged
merged 1 commit into from
Nov 10, 2020
Merged

Conversation

philipwalton
Copy link
Member
@philipwalton philipwalton commented Nov 10, 2020

This PR fixes #83 by reporting each of the user-centric metrics after a page has been restored from the back/forward cache (bfcache), in order to match the new behavior of the metrics as measured by the Chrome User Experience Report (CrUX).

Since dedicated Web APIs do not yet exist to handle bfcache restores, polyfills are used to approximate the values. The polyfills implemented here match those that will be used by CrUX.

Polyfill deteails

Below is an explanation of each of the polyfills used to measure each metric. See the code changes for full implementation details:

FCP

To measure FCP after a bfcache restore, this library takes the delta between the pageshow event's timestamp and a timestamp from calling performance.now() in the next rendered frame. To ensure the timestamp comes from the frame after the pageshow event, the polyfill actually calls requestAnimationFrame() twice (our testing found double-rAF to be more accurate than single-rAF in this case).

LCP

When a page is stored in the bfcache, a full snapshot of the entire DOM is captured in memory. That means when it's restored it doesn't have to "load" anything, and as a result the full viewport can be painted in a single frame. This means that LCP is generally exactly the same as FCP, so the polyfill here is the same as the one outlined above.

I say "generally" above because technically a page could make network requests in the pageshow event and update the DOM, though in practice that is quite rare. This polyfill does not attempt to account for such cases—erring on the side of reporting a faster LCP time.

FID

Prior to this PR, the web-vitals library would look for the presence of the FID polyfill as use that for browsers that don't support the Event Timing API. After this PR is merged, that library will be deprecated and a new version of the polyfill will be hosted in this repo. The new version will include support for measuring FID after a bfcache restore—largely reusing the existing polyfill logic.

The default build of the web-vitals library will bundle the new FID polyfill, but a custom build with the polyfill separated can also be used so developers who want cross-browser support for FID can inline the polyfill in the <head> of their documents. Both options are supported and usage instructions will be documented in the README when v1 is released.

CLS

Since the Layout Instability API reports layout shifts that occur throughout the lifespan of a page (including after it's restored from bfcache), it can be used without any specific polyfilling needed.

The only change in the case of a bfcache restore is the metric value will be reset to 0.

TTFB

Since TTFB measures network latency, it is not relevant to bfcache and thus no new values will be reported after a bfcache restore. This metric will continue to only be reported once per page load.

New Bundles

To accommodate for multiples different ways to load the web-vitals modules and polyfills, the following new bundles/builds are included in this PR, comprising the new "standard" and "base+polyfill" versions:

dist/web-vitals.js

~1.5K (gzipped)

An ES module bundle of all metric functions, without any extra polyfills to expand browser support. This is the "standard" version and is the simplest way to consume this library out of the box.

pkg.module points to this version.

dist/web-vitals.umd.js

~1.7K (gzipped)

A UMD version of dist/web-vitals.js (with the window.webVitals.* namespace).

pkg.main points to this version in order to support older node versions of tools that cannot import ES modules.

dist/web-vitals.base.js

~1.3K (gzipped)

An ES module bundle containing just the "base" part of the "base+polyfill" version. This script is designed to work with the "polyfill.js" script inlined in the <head> of the page.

dist/web-vitals.base.umd.js

~1.5K (gzipped)

A UMD version of dist/web-vitals.base.umd.js (with the window.webVitals.* namespace).

dist/polyfill.js

~0.5K (gzipped)

The companion polyfill code to be inlined in the <head> of your document if using dist/web-vitals.base.js or dist/web-vitals.base.umd.js.

The polyfill uses the global namespace window.webVitals.*, which matches those used by the UMD builds.

The benefit of using the external polyfill is wider browser support and more accurate handling of edge cases (e.g. if a page is loaded in the background but foregrounded before the web-vitals code runs).

Copy link
Member
@mmocny mmocny left a comment

Choose a reason for hiding this comment

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

I've only played with web-vitals.js for a bit, but this looks good to me!


if (po) {
onHidden(() => {
po.takeRecords().map(entryHandler as PerformanceEntryHandler);
po.disconnect();
}, true);
}

if (self.__WEB_VITALS_EXTERNAL_POLYFILL__) {
Copy link
Member

Choose a reason for hiding this comment

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

Trying to make sense of this block, and I think it is:

  • If there is an externally loaded FID polyfill, use that as a signal to always record BFCache restores, even if the browser does not support FID natively. (Still, use browser FID if available).
  • If there is no externally loaded FID polyfill, only record BFCache restores if the browser natively supports FID.

I have to ask: if folks have the web-vitals.js library loaded, and it now already comes with the FID polyfill, should we support recording always FID+BFCache restores? Or, is web-vitals.js expected to be loaded too late to capture initial FID reliably?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, what you've outlined in the bullet points is correct.

I have to ask: if folks have the web-vitals.js library loaded, and it now already comes with the FID polyfill, should we support recording always FID+BFCache restores? Or, is web-vitals.js expected to be loaded too late to capture initial FID reliably?

The latter, it could be loaded too late, which is why using the polyfill requires adding a snippet of code to the <head>. The polyfill does work in the bfcache restore case because the web-vitals bundle is already loaded at that point.

Also, since may not be obvious, the self.__WEB_VITALS_EXTERNAL_POLYFILL__ conditional blocks get stripped out at build time. They're used to build two versions of the the getFID() module.

import {onHidden} from './onHidden.js';

let firstHiddenTime = -1;
Copy link
Member

Choose a reason for hiding this comment

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

Curious, why the change from undefined?

Copy link
Member Author

Choose a reason for hiding this comment

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

TypeScript...

In this case it was easier to ensure the type stays consistent the whole time. Also it ended up being slightly smaller in terms of code size.

@philipwalton philipwalton merged commit 9943c4f into v1 Nov 10, 2020
@philipwalton philipwalton deleted the bfcache branch November 10, 2020 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants