-
Notifications
You must be signed in to change notification settings - Fork 27k
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
Adds first input delay performance metric #8884
Conversation
@housseindjirdeh could you fix the merge conflicts? @Timer had the concern that this PR increase bundles sizes. |
3992020
to
d1a54bd
Compare
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 12s | 12.4s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.48 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.44 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html | 4.06 kB | 6.75 kB | |
index.html gzip | 1.04 kB | 1.84 kB | |
link.html | 4.58 kB | 7.28 kB | |
link.html gzip | 1.12 kB | 1.92 kB | |
withRouter.html | 4.59 kB | 7.28 kB | |
withRouter.html gzip | 1.1 kB | 1.91 kB | |
Overall change | 13.2 kB | 21.3 kB |
Diffs
Diff for main-HASH.js
@@ -850,6 +850,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -889,11 +890,34 @@ function clearMarks() {
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(function (mark) {
return performance.clearMarks(mark);
});
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(function (measure) {
return performance.clearMeasures(measure);
});
}
+function measureFid() {
+ hydrationMetrics.onInputDelay(function (delay, event) {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration;
+ var hydrateEnd = startTime + duration; // TODO: Instead of console.logs, expose values to user code through perf relayer (next.js/pull/8480)
+
+ console.log('First input after hydration', "start: " + event.timeStamp, "delay: " + delay);
+ console.log('Delta between hydration end and first input', "diff: " + (event.timeStamp - hydrateEnd));
+ } else {
+ console.log('First input before hydration', "start: " + event.timeStamp, "delay: " + delay);
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var children = _ref5.children;
return _react["default"].createElement(Container, {
Diff for main-HASH.module.js
@@ -627,6 +627,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -664,9 +665,33 @@ function markRenderComplete() {
function clearMarks() {
;
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(mark => performance.clearMarks(mark));
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(measure => performance.clearMeasures(measure));
}
+function measureFid() {
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var {
+ startTime,
+ duration
+ } = hydrationMeasures[0];
+ var hydrateEnd = startTime + duration; // TODO: Instead of console.logs, expose values to user code through perf relayer (next.js/pull/8480)
+
+ console.log('First input after hydration', "start: " + event.timeStamp, "delay: " + delay);
+ console.log('Delta between hydration end and first input', "diff: " + (event.timeStamp - hydrateEnd));
+ } else {
+ console.log('First input before hydration', "start: " + event.timeStamp, "delay: " + delay);
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var {
children
Diff for index.html
@@ -12,7 +12,7 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-4b0140f328329d8c4fe7.module.js"
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/framework.9c9aa574c484a7d0240e.module.js"
as="script" crossorigin="anonymous" />
@@ -20,6 +20,94 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/de003c3a9d308750aa009870a5926f9b18ab31f4.dda3a797bb804537ed85.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -44,9 +132,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-f0e3b16a229bdd59944b.js"
+ <script src="/_next/static/runtime/main-c75707e0b5c454789372.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <script src="/_next/static/runtime/main-4b0140f328329d8c4fe7.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/chunks/framework.4c64484d8a631a55b435.js"
async="" crossorigin="anonymous" nomodule=""></script>
Diff for link.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-4b0140f328329d8c4fe7.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -67,9 +155,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-f0e3b16a229bdd59944b.js"
+ <script src="/_next/static/runtime/main-c75707e0b5c454789372.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <script src="/_next/static/runtime/main-4b0140f328329d8c4fe7.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Diff for withRouter.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-4b0140f328329d8c4fe7.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -65,9 +153,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-f0e3b16a229bdd59944b.js"
+ <script src="/_next/static/runtime/main-c75707e0b5c454789372.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <script src="/_next/static/runtime/main-4b0140f328329d8c4fe7.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 12.6s | 13.1s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.48 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.44 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js | 255 kB | 259 kB | |
_error.js gzip | 67.9 kB | 68.7 kB | |
hooks.html | 4.19 kB | 6.89 kB | |
hooks.html gzip | 1.07 kB | 1.86 kB | |
index.js | 256 kB | 259 kB | |
index.js gzip | 68 kB | 68.9 kB | |
link.js | 288 kB | 291 kB | |
link.js gzip | 77.1 kB | 77.9 kB | |
routerDirect.js | 281 kB | 284 kB | |
routerDirect.js gzip | 75.1 kB | 75.9 kB | |
withRouter.js | 281 kB | 284 kB | |
withRouter.js gzip | 75.2 kB | 76 kB | |
Overall change | 1.37 MB | 1.38 MB |
Commit: d1a54bd
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 13.4s | 13.9s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html | 4.06 kB | 6.75 kB | |
index.html gzip | 1.04 kB | 1.83 kB | |
link.html | 4.58 kB | 7.28 kB | |
link.html gzip | 1.12 kB | 1.92 kB | |
withRouter.html | 4.59 kB | 7.28 kB | |
withRouter.html gzip | 1.1 kB | 1.91 kB | |
Overall change | 13.2 kB | 21.3 kB |
Diffs
Diff for main-HASH.js
@@ -850,6 +850,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -889,11 +890,50 @@ function clearMarks() {
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(function (mark) {
return performance.clearMarks(mark);
});
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(function (measure) {
return performance.clearMeasures(measure);
});
}
+function measureFid() {
+ hydrationMetrics.onInputDelay(function (delay, event) {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration;
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var children = _ref5.children;
return _react["default"].createElement(Container, {
Diff for main-HASH.module.js
@@ -627,6 +627,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -664,9 +665,49 @@ function markRenderComplete() {
function clearMarks() {
;
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(mark => performance.clearMarks(mark));
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(measure => performance.clearMeasures(measure));
}
+function measureFid() {
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var {
+ startTime,
+ duration
+ } = hydrationMeasures[0];
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var {
children
Diff for index.html
@@ -12,7 +12,7 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-b9043a785a9af4746b1e.module.js"
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/framework.9c9aa574c484a7d0240e.module.js"
as="script" crossorigin="anonymous" />
@@ -20,6 +20,94 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/de003c3a9d308750aa009870a5926f9b18ab31f4.dda3a797bb804537ed85.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -44,9 +132,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-f0e3b16a229bdd59944b.js"
+ <script src="/_next/static/runtime/main-a009e59d09246fa2b410.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <script src="/_next/static/runtime/main-b9043a785a9af4746b1e.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/chunks/framework.4c64484d8a631a55b435.js"
async="" crossorigin="anonymous" nomodule=""></script>
Diff for link.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-b9043a785a9af4746b1e.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -67,9 +155,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-f0e3b16a229bdd59944b.js"
+ <script src="/_next/static/runtime/main-a009e59d09246fa2b410.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <script src="/_next/static/runtime/main-b9043a785a9af4746b1e.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Diff for withRouter.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-b9043a785a9af4746b1e.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -65,9 +153,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-f0e3b16a229bdd59944b.js"
+ <script src="/_next/static/runtime/main-a009e59d09246fa2b410.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-0e1c4b24306318fc1d32.module.js"
+ <script src="/_next/static/runtime/main-b9043a785a9af4746b1e.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 13.7s | 14.1s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js | 255 kB | 259 kB | |
_error.js gzip | 67.9 kB | 68.7 kB | |
hooks.html | 4.19 kB | 6.89 kB | |
hooks.html gzip | 1.07 kB | 1.86 kB | |
index.js | 256 kB | 259 kB | |
index.js gzip | 68 kB | 68.9 kB | |
link.js | 288 kB | 291 kB | |
link.js gzip | 77.1 kB | 77.9 kB | |
routerDirect.js | 281 kB | 284 kB | |
routerDirect.js gzip | 75.1 kB | 75.9 kB | |
withRouter.js | 281 kB | 284 kB | |
withRouter.js gzip | 75.2 kB | 76 kB | |
Overall change | 1.37 MB | 1.38 MB |
Commit: bd9ac98
a9fdd5f
to
a8c985c
Compare
packages/next/client/index.js
Outdated
const { startTime, duration } = hydrationMeasures[0] | ||
const hydrateEnd = startTime + duration | ||
|
||
if (onPerfEntry) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CC @prateekbh
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 15.5s | 15.9s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html | 4.06 kB | 6.75 kB | |
index.html gzip | 1.04 kB | 1.83 kB | |
link.html | 4.58 kB | 7.28 kB | |
link.html gzip | 1.12 kB | 1.92 kB | |
withRouter.html | 4.59 kB | 7.28 kB | |
withRouter.html gzip | 1.1 kB | 1.9 kB | |
Overall change | 13.2 kB | 21.3 kB |
Diffs
Diff for main-HASH.js
@@ -428,7 +428,7 @@ var _dataManager = __webpack_require__("0IRE");
var _querystring = __webpack_require__("s4NR");
var _isDynamic = __webpack_require__("/jkW");
-/* global location */
+/* global location, hydrationMetrics */
if (!window.Promise) {
@@ -850,6 +850,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -889,11 +890,50 @@ function clearMarks() {
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(function (mark) {
return performance.clearMarks(mark);
});
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(function (measure) {
return performance.clearMeasures(measure);
});
}
+function measureFid() {
+ hydrationMetrics.onInputDelay(function (delay, event) {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration;
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var children = _ref5.children;
return _react["default"].createElement(Container, {
Diff for main-HASH.module.js
@@ -332,7 +332,7 @@ var _dataManager = __webpack_require__("0IRE");
var _querystring = __webpack_require__("s4NR");
var _isDynamic = __webpack_require__("/jkW");
-/* global location */
+/* global location, hydrationMetrics */
if (!window.Promise) {
@@ -627,6 +627,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -664,9 +665,49 @@ function markRenderComplete() {
function clearMarks() {
;
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(mark => performance.clearMarks(mark));
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(measure => performance.clearMeasures(measure));
}
+function measureFid() {
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var {
+ startTime,
+ duration
+ } = hydrationMeasures[0];
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var {
children
Diff for index.html
@@ -12,7 +12,7 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/framework.9c9aa574c484a7d0240e.module.js"
as="script" crossorigin="anonymous" />
@@ -20,6 +20,94 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/de003c3a9d308750aa009870a5926f9b18ab31f4.dda3a797bb804537ed85.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -44,9 +132,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/chunks/framework.4c64484d8a631a55b435.js"
async="" crossorigin="anonymous" nomodule=""></script>
Diff for link.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -67,9 +155,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Diff for withRouter.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -65,9 +153,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 15.7s | 16.1s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js | 255 kB | 259 kB | |
_error.js gzip | 67.9 kB | 68.7 kB | |
hooks.html | 4.19 kB | 6.89 kB | |
hooks.html gzip | 1.07 kB | 1.86 kB | |
index.js | 256 kB | 259 kB | |
index.js gzip | 68 kB | 68.9 kB | |
link.js | 288 kB | 291 kB | |
link.js gzip | 77.1 kB | 77.9 kB | |
routerDirect.js | 281 kB | 284 kB | |
routerDirect.js gzip | 75.1 kB | 75.9 kB | |
withRouter.js | 281 kB | 284 kB | |
withRouter.js gzip | 75.2 kB | 76 kB | |
Overall change | 1.37 MB | 1.38 MB |
Commit: a8c985c
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 15.2s | 15.2s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html | 4.06 kB | 6.75 kB | |
index.html gzip | 1.04 kB | 1.83 kB | |
link.html | 4.58 kB | 7.28 kB | |
link.html gzip | 1.12 kB | 1.92 kB | |
withRouter.html | 4.59 kB | 7.28 kB | |
withRouter.html gzip | 1.1 kB | 1.9 kB | |
Overall change | 13.2 kB | 21.3 kB |
Diffs
Diff for main-HASH.js
@@ -428,7 +428,7 @@ var _dataManager = __webpack_require__("0IRE");
var _querystring = __webpack_require__("s4NR");
var _isDynamic = __webpack_require__("/jkW");
-/* global location */
+/* global location, hydrationMetrics */
if (!window.Promise) {
@@ -850,6 +850,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -889,11 +890,50 @@ function clearMarks() {
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(function (mark) {
return performance.clearMarks(mark);
});
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(function (measure) {
return performance.clearMeasures(measure);
});
}
+function measureFid() {
+ hydrationMetrics.onInputDelay(function (delay, event) {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration;
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var children = _ref5.children;
return _react["default"].createElement(Container, {
Diff for main-HASH.module.js
@@ -332,7 +332,7 @@ var _dataManager = __webpack_require__("0IRE");
var _querystring = __webpack_require__("s4NR");
var _isDynamic = __webpack_require__("/jkW");
-/* global location */
+/* global location, hydrationMetrics */
if (!window.Promise) {
@@ -627,6 +627,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -664,9 +665,49 @@ function markRenderComplete() {
function clearMarks() {
;
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(mark => performance.clearMarks(mark));
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(measure => performance.clearMeasures(measure));
}
+function measureFid() {
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var {
+ startTime,
+ duration
+ } = hydrationMeasures[0];
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var {
children
Diff for index.html
@@ -12,7 +12,7 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/framework.9c9aa574c484a7d0240e.module.js"
as="script" crossorigin="anonymous" />
@@ -20,6 +20,94 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/de003c3a9d308750aa009870a5926f9b18ab31f4.dda3a797bb804537ed85.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -44,9 +132,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/chunks/framework.4c64484d8a631a55b435.js"
async="" crossorigin="anonymous" nomodule=""></script>
Diff for link.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -67,9 +155,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Diff for withRouter.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type == 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -65,9 +153,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 15.9s | 15.8s | -81ms |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js | 255 kB | 259 kB | |
_error.js gzip | 67.9 kB | 68.7 kB | |
hooks.html | 4.19 kB | 6.89 kB | |
hooks.html gzip | 1.07 kB | 1.86 kB | |
index.js | 256 kB | 259 kB | |
index.js gzip | 68 kB | 68.9 kB | |
link.js | 288 kB | 291 kB | |
link.js gzip | 77.1 kB | 77.9 kB | |
routerDirect.js | 281 kB | 284 kB | |
routerDirect.js gzip | 75.1 kB | 75.9 kB | |
withRouter.js | 281 kB | 284 kB | |
withRouter.js gzip | 75.2 kB | 76 kB | |
Overall change | 1.37 MB | 1.38 MB |
Commit: a8c985c
a8c985c
to
0744279
Compare
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 13.9s | 14.2s | |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html | 4.06 kB | 6.75 kB | |
index.html gzip | 1.04 kB | 1.83 kB | |
link.html | 4.58 kB | 7.28 kB | |
link.html gzip | 1.12 kB | 1.92 kB | |
withRouter.html | 4.59 kB | 7.29 kB | |
withRouter.html gzip | 1.1 kB | 1.91 kB | |
Overall change | 13.2 kB | 21.3 kB |
Diffs
Diff for main-HASH.js
@@ -428,7 +428,7 @@ var _dataManager = __webpack_require__("0IRE");
var _querystring = __webpack_require__("s4NR");
var _isDynamic = __webpack_require__("/jkW");
-/* global location */
+/* global location, hydrationMetrics */
if (!window.Promise) {
@@ -850,6 +850,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -889,11 +890,50 @@ function clearMarks() {
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(function (mark) {
return performance.clearMarks(mark);
});
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(function (measure) {
return performance.clearMeasures(measure);
});
}
+function measureFid() {
+ hydrationMetrics.onInputDelay(function (delay, event) {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration;
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var children = _ref5.children;
return _react["default"].createElement(Container, {
Diff for main-HASH.module.js
@@ -332,7 +332,7 @@ var _dataManager = __webpack_require__("0IRE");
var _querystring = __webpack_require__("s4NR");
var _isDynamic = __webpack_require__("/jkW");
-/* global location */
+/* global location, hydrationMetrics */
if (!window.Promise) {
@@ -627,6 +627,7 @@ function renderReactElement(reactEl, domEl) {
function markHydrateComplete() {
if (!_utils.SUPPORTS_PERFORMANCE_USER_TIMING) return;
+ measureFid();
performance.mark('afterHydrate'); // mark end of hydration
performance.measure('Next.js-before-hydration', 'navigationStart', 'beforeRender');
@@ -664,9 +665,49 @@ function markRenderComplete() {
function clearMarks() {
;
['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(mark => performance.clearMarks(mark));
+}
+
+function clearMeasures() {
+ ;
['Next.js-before-hydration', 'Next.js-hydration', 'Next.js-route-change-to-render', 'Next.js-render'].forEach(measure => performance.clearMeasures(measure));
}
+function measureFid() {
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+
+ if (hydrationMeasures.length > 0) {
+ var {
+ startTime,
+ duration
+ } = hydrationMeasures[0];
+ var hydrateEnd = startTime + duration;
+
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd
+ });
+ }
+ } else {
+ if (onPerfEntry) {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay
+ });
+ }
+ }
+ });
+ clearMeasures();
+}
+
function AppContainer(_ref5) {
var {
children
Diff for index.html
@@ -12,7 +12,7 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/framework.9c9aa574c484a7d0240e.module.js"
as="script" crossorigin="anonymous" />
@@ -20,6 +20,94 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/de003c3a9d308750aa009870a5926f9b18ab31f4.dda3a797bb804537ed85.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -44,9 +132,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/runtime/webpack-7928590be3ef2e55b835.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/chunks/framework.4c64484d8a631a55b435.js"
async="" crossorigin="anonymous" nomodule=""></script>
Diff for link.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -67,9 +155,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Diff for withRouter.html
@@ -20,8 +20,96 @@
as="script" crossorigin="anonymous" />
<link rel="preload" href="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
as="script" crossorigin="anonymous" />
- <link rel="preload" href="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <link rel="preload" href="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
as="script" crossorigin="anonymous" />
+ <script>
+ (function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent;
+ var firstInputDelay;
+ var firstInputTimeStamp;
+ var callbacks = [];
+ var listenerOpts = {
+ passive: true,
+ capture: true
+ };
+ var startTimeStamp = +new Date();
+ var pointerup = 'pointerup';
+ var pointercancel = 'pointercancel';
+
+ function onInputDelay(callback) {
+ callbacks.push(callback);
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt;
+ firstInputDelay = delay;
+ firstInputTimeStamp = +new Date();
+ reportInputDelayIfRecordedAndValid();
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName('Next.js-hydration', 'measure');
+ var firstInputStart = firstInputTimeStamp - startTimeStamp;
+
+ if (firstInputDelay >= 0 && firstInputDelay < firstInputStart && (hydrationMeasures.length === 0 || hydrationMeasures[0].startTime < firstInputStart)) {
+ callbacks.forEach(function (callback) {
+ callback(firstInputDelay, firstInputEvent);
+ }); // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener);
+ callbacks = [];
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt);
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners();
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts);
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts);
+ addEventListener(pointercancel, onPointerCancel, listenerOpts);
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12;
+ var now = isEpochTime ? +new Date() : performance.now();
+ var delay = now - evt.timeStamp;
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt);
+ } else {
+ recordInputDelay(delay, evt);
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = ['click', 'mousedown', 'keydown', 'touchstart', 'pointerdown'];
+ eventTypes.forEach(function (eventType) {
+ callback(eventType, onInput, listenerOpts);
+ });
+ }
+
+ eachEventType(addEventListener);
+ var context = self;
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {};
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay;
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
@@ -65,9 +153,9 @@
async="" crossorigin="anonymous" nomodule=""></script>
<script src="/_next/static/chunks/16b1a72255206b7853bf6603dc58ec83de39b142.5f63c642fc68b85bc4ba.module.js"
async="" crossorigin="anonymous" type="module"></script>
- <script src="/_next/static/runtime/main-cfa30afa3978aa8061c5.js"
+ <script src="/_next/static/runtime/main-e008657343e015483bcd.js"
async="" crossorigin="anonymous" nomodule=""></script>
- <script src="/_next/static/runtime/main-2360a070726bccc86ce4.module.js"
+ <script src="/_next/static/runtime/main-62a58a0edbf83429b9b4.module.js"
async="" crossorigin="anonymous" type="module"></script>
<script src="/_next/static/BUILD_ID/_buildManifest.js" async=""
crossorigin="anonymous" nomodule=""></script>
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 14.6s | 14.5s | -133ms |
nodeModulesSize | 48.3 MB | 48.3 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js | 17.9 kB | 18.3 kB | |
main-HASH.js gzip | 6.33 kB | 6.46 kB | |
webpack-HASH.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..ebc17c08d.js | 19.8 kB | 19.8 kB | ✓ |
16b1a7225520..c08d.js gzip | 7.35 kB | 7.35 kB | ✓ |
4952ddcd88e7..49d7903c2.js | 9.6 kB | 9.6 kB | ✓ |
4952ddcd88e7..03c2.js gzip | 3.76 kB | 3.76 kB | ✓ |
commons.HASH.js | 10.4 kB | 10.4 kB | ✓ |
commons.HASH.js gzip | 3.91 kB | 3.91 kB | ✓ |
de003c3a9d30..a824c82eb.js | 28.3 kB | 28.3 kB | ✓ |
de003c3a9d30..82eb.js gzip | 10.7 kB | 10.7 kB | ✓ |
framework.HASH.js | 125 kB | 125 kB | ✓ |
framework.HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 213 kB | 213 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js | 14.1 kB | 14.5 kB | |
main-HASH.module.js gzip | 5.3 kB | 5.42 kB | |
webpack-HASH.module.js | 1.53 kB | 1.53 kB | ✓ |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
16b1a7225520..9a.module.js | 21.3 kB | 21.3 kB | ✓ |
16b1a7225520..dule.js gzip | 8.14 kB | 8.14 kB | ✓ |
4952ddcd88e7..cc.module.js | 11.9 kB | 11.9 kB | ✓ |
4952ddcd88e7..dule.js gzip | 4.43 kB | 4.43 kB | ✓ |
de003c3a9d30..36.module.js | 22.2 kB | 22.2 kB | ✓ |
de003c3a9d30..dule.js gzip | 8.71 kB | 8.71 kB | ✓ |
framework.HASH.module.js | 125 kB | 125 kB | ✓ |
framework.HA..dule.js gzip | 39.4 kB | 39.4 kB | ✓ |
Overall change | 196 kB | 197 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js | 9.11 kB | 9.11 kB | ✓ |
polyfills-HASH.js gzip | 3.11 kB | 3.11 kB | ✓ |
Overall change | 9.11 kB | 9.11 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js | 2.92 kB | 2.92 kB | ✓ |
_app.js gzip | 1.32 kB | 1.32 kB | ✓ |
_error.js | 13.2 kB | 13.2 kB | ✓ |
_error.js gzip | 5.1 kB | 5.1 kB | ✓ |
hooks.js | 1.92 kB | 1.92 kB | ✓ |
hooks.js gzip | 941 B | 941 B | ✓ |
index.js | 318 B | 318 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js | 6.76 kB | 6.76 kB | ✓ |
link.js gzip | 2.88 kB | 2.88 kB | ✓ |
routerDirect.js | 413 B | 413 B | ✓ |
routerDirect.js gzip | 285 B | 285 B | ✓ |
withRouter.js | 423 B | 423 B | ✓ |
withRouter.js gzip | 284 B | 284 B | ✓ |
Overall change | 26 kB | 26 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js | 1.54 kB | 1.54 kB | ✓ |
_app.module.js gzip | 759 B | 759 B | ✓ |
_error.module.js | 10.5 kB | 10.5 kB | ✓ |
_error.module.js gzip | 4.26 kB | 4.26 kB | ✓ |
hooks.module.js | 677 B | 677 B | ✓ |
hooks.module.js gzip | 384 B | 384 B | ✓ |
index.module.js | 292 B | 292 B | ✓ |
index.module.js gzip | 223 B | 223 B | ✓ |
link.module.js | 5.49 kB | 5.49 kB | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect.module.js | 399 B | 399 B | ✓ |
routerDirect..dule.js gzip | 285 B | 285 B | ✓ |
withRouter.module.js | 409 B | 409 B | ✓ |
withRouter.m..dule.js gzip | 282 B | 282 B | ✓ |
Overall change | 19.3 kB | 19.3 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js | 244 B | 244 B | ✓ |
_buildManifest.js gzip | 201 B | 201 B | ✓ |
_buildManifest.module.js | 251 B | 251 B | ✓ |
_buildManife..dule.js gzip | 208 B | 208 B | ✓ |
Overall change | 495 B | 495 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js | 255 kB | 259 kB | |
_error.js gzip | 67.9 kB | 68.7 kB | |
hooks.html | 4.19 kB | 6.89 kB | |
hooks.html gzip | 1.07 kB | 1.86 kB | |
index.js | 256 kB | 259 kB | |
index.js gzip | 68 kB | 68.9 kB | |
link.js | 288 kB | 291 kB | |
link.js gzip | 77.1 kB | 77.9 kB | |
routerDirect.js | 281 kB | 284 kB | |
routerDirect.js gzip | 75.1 kB | 75.9 kB | |
withRouter.js | 281 kB | 284 kB | |
withRouter.js gzip | 75.2 kB | 76 kB | |
Overall change | 1.37 MB | 1.38 MB |
Commit: 0744279
@timneutkens resolved @Timer I noticed :(. Surprised that primarily just a single method ( |
packages/next/pages/_document.tsx
Outdated
@@ -518,6 +519,11 @@ export class Head extends Component< | |||
</> | |||
)} | |||
{React.createElement(React.Fragment, {}, ...(headTags || []))} | |||
<script |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the very first <script>
after <link rel=preload>
but before the first app <script defer/async>
.
h/t @developit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created a helper render method to make sure it's before the only async app script (in AMP mode) 👍
70f6c79
to
0d4d039
Compare
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 15.1s | 14.8s | -210ms |
nodeModulesSize | 48.9 MB | 48.9 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js gzip | 5.11 kB | 5.28 kB | |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..54d3.js gzip | 4.68 kB | 4.68 kB | ✓ |
commons.HASH.js gzip | 4.06 kB | 4.06 kB | ✓ |
de003c3a9d30..9881.js gzip | 13.7 kB | 13.7 kB | ✓ |
framework.HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 67.4 kB | 67.6 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js gzip | 4.17 kB | 4.34 kB | |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..dule.js gzip | 5.56 kB | 5.56 kB | ✓ |
de003c3a9d30..dule.js gzip | 12.5 kB | 12.5 kB | ✓ |
framework.HA..dule.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 62.1 kB | 62.2 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 4.76 kB | 4.76 kB | ✓ |
Overall change | 4.76 kB | 4.76 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js gzip | 1.33 kB | 1.33 kB | ✓ |
_error.js gzip | 4.07 kB | 4.07 kB | ✓ |
hooks.js gzip | 779 B | 779 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js gzip | 2.9 kB | 2.9 kB | ✓ |
routerDirect.js gzip | 283 B | 283 B | ✓ |
withRouter.js gzip | 282 B | 282 B | ✓ |
Overall change | 9.87 kB | 9.87 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js gzip | 757 B | 757 B | ✓ |
_error.module.js gzip | 3.06 kB | 3.06 kB | ✓ |
hooks.module.js gzip | 371 B | 371 B | ✓ |
index.module.js gzip | 212 B | 212 B | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect..dule.js gzip | 273 B | 273 B | ✓ |
withRouter.m..dule.js gzip | 272 B | 272 B | ✓ |
Overall change | 7.41 kB | 7.41 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js gzip | 61 B | 61 B | ✓ |
_buildManife..dule.js gzip | 61 B | 61 B | ✓ |
Overall change | 122 B | 122 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html gzip | 1.02 kB | 1.82 kB | |
link.html gzip | 1.03 kB | 1.83 kB | |
withRouter.html gzip | 1.01 kB | 1.81 kB | |
Overall change | 3.06 kB | 5.46 kB |
Diffs
Diff for main-HASH.js
@@ -571,33 +571,28 @@
_ref3 = _context.sent
app = _ref3.page
mod = _ref3.mod
- App = app
+ App = app // if (mod && mod.unstable_onPerformanceData) {
+ // onPerfEntry = function({ name, startTime, value, duration }) {
+ // mod.unstable_onPerformanceData({ name, startTime, value, duration })
+ // }
+ // }
+
+ onPerfEntry = false
if (mod && mod.unstable_onPerformanceData) {
- onPerfEntry = function onPerfEntry(_ref3) {
- var name = _ref3.name,
- startTime = _ref3.startTime,
- value = _ref3.value,
- duration = _ref3.duration
- mod.unstable_onPerformanceData({
- name: name,
- startTime: startTime,
- value: value,
- duration: duration,
- })
- }
+ onPerfEntry = false
}
initialErr = err
- _context.prev = 10
- _context.next = 13
+ _context.prev = 11
+ _context.next = 14
return pageLoader.loadPage(page)
- case 13:
+ case 14:
Component = _context.sent
if (true) {
- _context.next = 18
+ _context.next = 19
break
}
@@ -611,7 +606,7 @@
(isValidElementType = _require.isValidElementType)
if (isValidElementType(Component)) {
- _context.next = 18
+ _context.next = 19
break
}
@@ -621,26 +616,26 @@
'"'
)
- case 18:
- _context.next = 23
+ case 19:
+ _context.next = 24
break
- case 20:
- _context.prev = 20
- _context.t0 = _context['catch'](10)
+ case 21:
+ _context.prev = 21
+ _context.t0 = _context['catch'](11)
// This catches errors like throwing in the top level of a module
initialErr = _context.t0
- case 23:
+ case 24:
if (!window.__NEXT_PRELOADREADY) {
- _context.next = 26
+ _context.next = 27
break
}
- _context.next = 26
+ _context.next = 27
return window.__NEXT_PRELOADREADY(dynamicIds)
- case 26:
+ case 27:
exports.router = router = (0, _router.createRouter)(
page,
query,
@@ -652,10 +647,10 @@
Component: Component,
wrapApp: wrapApp,
err: initialErr,
- subscription: function subscription(_ref4, App) {
- var Component = _ref4.Component,
- props = _ref4.props,
- err = _ref4.err
+ subscription: function subscription(_ref3, App) {
+ var Component = _ref3.Component,
+ props = _ref3.props,
+ err = _ref3.err
render({
App: App,
Component: Component,
@@ -678,7 +673,7 @@
render(renderCtx)
return _context.abrupt('return', emitter)
- case 31:
+ case 32:
case 'end':
return _context.stop()
}
@@ -686,7 +681,7 @@
},
_callee,
null,
- [[10, 20]]
+ [[11, 21]]
)
})
)
@@ -899,8 +894,24 @@
'beforeRender'
)
performance.measure('Next.js-hydration', 'beforeRender', 'afterHydrate')
+ console.log(onPerfEntry, 'eve')
if (onPerfEntry) {
+ console.log('noteven')
+
+ _promise['default']
+ .resolve()
+ .then(function() {
+ return (0,
+ _interopRequireWildcard2['default'])(__webpack_require__('dT2I'))
+ })
+ .then(function(mod) {
+ mod['default'](onPerfEntry)
+ })
+ ['catch'](function(err) {
+ console.error('Error measuring FID', err)
+ })
+
performance.getEntriesByName('Next.js-hydration').forEach(onPerfEntry)
performance.getEntriesByName('beforeRender').forEach(onPerfEntry)
}
@@ -944,18 +955,10 @@
return performance.clearMarks(mark)
}
)
- ;[
- 'Next.js-before-hydration',
- 'Next.js-hydration',
- 'Next.js-route-change-to-render',
- 'Next.js-render',
- ].forEach(function(measure) {
- return performance.clearMeasures(measure)
- })
}
- function AppContainer(_ref5) {
- var children = _ref5.children
+ function AppContainer(_ref4) {
+ var children = _ref4.children
return _react['default'].createElement(
Container,
{
@@ -1006,7 +1009,7 @@
function _doRender() {
_doRender = (0, _asyncToGenerator2['default'])(
/*#__PURE__*/
- _regeneratorRuntime.mark(function _callee4(_ref6) {
+ _regeneratorRuntime.mark(function _callee4(_ref5) {
var App,
Component,
props,
@@ -1024,10 +1027,10 @@
while (1) {
switch ((_context4.prev = _context4.next)) {
case 0:
- ;(App = _ref6.App),
- (Component = _ref6.Component),
- (props = _ref6.props),
- (err = _ref6.err) // Usual getInitialProps fetching is handled in next/router
+ ;(App = _ref5.App),
+ (Component = _ref5.Component),
+ (props = _ref5.props),
+ (err = _ref5.err) // Usual getInitialProps fetching is handled in next/router
// this is for when ErrorComponent gets replaced by Component by HMR
if (
@@ -1174,6 +1177,67 @@
/***/
},
+ /***/ dT2I: /***/ function(module, exports, __webpack_require__) {
+ 'use strict'
+
+ /* global hydrationMetrics */
+
+ var _Object$defineProperty = __webpack_require__('hfKm')
+
+ _Object$defineProperty(exports, '__esModule', {
+ value: true,
+ })
+
+ exports['default'] = function(onPerfEntry) {
+ function clearMeasures() {
+ ;[
+ 'Next.js-hydration',
+ 'Next.js-before-hydration',
+ 'Next.js-route-change-to-render',
+ 'Next.js-render',
+ ].forEach(function(measure) {
+ return performance.clearMeasures(measure)
+ })
+ }
+
+ hydrationMetrics.onInputDelay(function(delay, event) {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration
+ var hydrateEnd = startTime + duration
+
+ if (event.timeStamp > hydrateEnd) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd,
+ })
+ clearMeasures()
+ } else {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ }
+ }
+ })
+ }
+
+ /***/
+ },
+
/***/ yLiY: /***/ function(module, exports, __webpack_require__) {
'use strict'
Diff for main-HASH.module.js
@@ -418,18 +418,16 @@
}
var { page: app, mod } = yield pageLoader.loadPageScript('/_app')
- App = app
+ App = app // if (mod && mod.unstable_onPerformanceData) {
+ // onPerfEntry = function({ name, startTime, value, duration }) {
+ // mod.unstable_onPerformanceData({ name, startTime, value, duration })
+ // }
+ // }
+
+ onPerfEntry = false
if (mod && mod.unstable_onPerformanceData) {
- onPerfEntry = function onPerfEntry(_ref3) {
- var { name, startTime, value, duration } = _ref3
- mod.unstable_onPerformanceData({
- name,
- startTime,
- value,
- duration,
- })
- }
+ onPerfEntry = false
}
var initialErr = err
@@ -460,8 +458,8 @@
Component,
wrapApp,
err: initialErr,
- subscription: (_ref4, App) => {
- var { Component, props, err } = _ref4
+ subscription: (_ref3, App) => {
+ var { Component, props, err } = _ref3
render({
App,
Component,
@@ -613,8 +611,23 @@
'beforeRender'
)
performance.measure('Next.js-hydration', 'beforeRender', 'afterHydrate')
+ console.log(onPerfEntry, 'eve')
if (onPerfEntry) {
+ console.log('noteven')
+
+ _promise.default
+ .resolve()
+ .then(() =>
+ (0, _interopRequireWildcard2.default)(__webpack_require__('dT2I'))
+ )
+ .then(mod => {
+ mod.default(onPerfEntry)
+ })
+ .catch(err => {
+ console.error('Error measuring FID', err)
+ })
+
performance.getEntriesByName('Next.js-hydration').forEach(onPerfEntry)
performance.getEntriesByName('beforeRender').forEach(onPerfEntry)
}
@@ -656,16 +669,10 @@
;['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(
mark => performance.clearMarks(mark)
)
- ;[
- 'Next.js-before-hydration',
- 'Next.js-hydration',
- 'Next.js-route-change-to-render',
- 'Next.js-render',
- ].forEach(measure => performance.clearMeasures(measure))
}
- function AppContainer(_ref5) {
- var { children } = _ref5
+ function AppContainer(_ref4) {
+ var { children } = _ref4
return _react.default.createElement(
Container,
{
@@ -709,8 +716,8 @@
}
function _doRender() {
- _doRender = (0, _asyncToGenerator2.default)(function*(_ref6) {
- var { App, Component, props, err } = _ref6 // Usual getInitialProps fetching is handled in next/router
+ _doRender = (0, _asyncToGenerator2.default)(function*(_ref5) {
+ var { App, Component, props, err } = _ref5 // Usual getInitialProps fetching is handled in next/router
// this is for when ErrorComponent gets replaced by Component by HMR
if (
@@ -770,6 +777,63 @@
/***/
},
+ /***/ dT2I: /***/ function(module, exports, __webpack_require__) {
+ 'use strict'
+
+ /* global hydrationMetrics */
+
+ var _Object$defineProperty = __webpack_require__('hfKm')
+
+ _Object$defineProperty(exports, '__esModule', {
+ value: true,
+ })
+
+ exports.default = onPerfEntry => {
+ function clearMeasures() {
+ ;[
+ 'Next.js-hydration',
+ 'Next.js-before-hydration',
+ 'Next.js-route-change-to-render',
+ 'Next.js-render',
+ ].forEach(measure => performance.clearMeasures(measure))
+ }
+
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+
+ if (hydrationMeasures.length > 0) {
+ var { startTime, duration } = hydrationMeasures[0]
+ var hydrateEnd = startTime + duration
+
+ if (event.timeStamp > hydrateEnd) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd,
+ })
+ clearMeasures()
+ } else {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ }
+ }
+ })
+ }
+
+ /***/
+ },
+
/***/ yLiY: /***/ function(module, exports, __webpack_require__) {
'use strict'
Diff for index.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-6c5edf42e2a83b8e821d.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next">Hello world 👋</div>
@@ -163,13 +265,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-72e4ccc4092f95056ddd.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-6c5edf42e2a83b8e821d.module.js"
defer=""
crossorigin="anonymous"
type="module"
Diff for link.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-6c5edf42e2a83b8e821d.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next">
@@ -168,13 +270,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-72e4ccc4092f95056ddd.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-6c5edf42e2a83b8e821d.module.js"
defer=""
crossorigin="anonymous"
type="module"
Diff for withRouter.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-6c5edf42e2a83b8e821d.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next"><div>I use withRouter</div></div>
@@ -163,13 +265,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-72e4ccc4092f95056ddd.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-6c5edf42e2a83b8e821d.module.js"
defer=""
crossorigin="anonymous"
type="module"
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 15.1s | 15.2s | |
nodeModulesSize | 48.9 MB | 48.9 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js gzip | 5.11 kB | 5.28 kB | |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..54d3.js gzip | 4.68 kB | 4.68 kB | ✓ |
commons.HASH.js gzip | 4.06 kB | 4.06 kB | ✓ |
de003c3a9d30..9881.js gzip | 13.7 kB | 13.7 kB | ✓ |
framework.HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 67.4 kB | 67.6 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js gzip | 4.17 kB | 4.34 kB | |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..dule.js gzip | 5.56 kB | 5.56 kB | ✓ |
de003c3a9d30..dule.js gzip | 12.5 kB | 12.5 kB | ✓ |
framework.HA..dule.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 62.1 kB | 62.2 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 4.76 kB | 4.76 kB | ✓ |
Overall change | 4.76 kB | 4.76 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js gzip | 1.33 kB | 1.33 kB | ✓ |
_error.js gzip | 4.07 kB | 4.07 kB | ✓ |
hooks.js gzip | 779 B | 779 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js gzip | 2.9 kB | 2.9 kB | ✓ |
routerDirect.js gzip | 283 B | 283 B | ✓ |
withRouter.js gzip | 282 B | 282 B | ✓ |
Overall change | 9.87 kB | 9.87 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js gzip | 757 B | 757 B | ✓ |
_error.module.js gzip | 3.06 kB | 3.06 kB | ✓ |
hooks.module.js gzip | 371 B | 371 B | ✓ |
index.module.js gzip | 212 B | 212 B | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect..dule.js gzip | 273 B | 273 B | ✓ |
withRouter.m..dule.js gzip | 272 B | 272 B | ✓ |
Overall change | 7.41 kB | 7.41 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js gzip | 61 B | 61 B | ✓ |
_buildManife..dule.js gzip | 61 B | 61 B | ✓ |
Overall change | 122 B | 122 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js gzip | 77.8 kB | 78.6 kB | |
hooks.html gzip | 1.05 kB | 1.85 kB | |
index.js gzip | 78 kB | 78.8 kB | |
link.js gzip | 80.4 kB | 81.2 kB | |
routerDirect.js gzip | 78 kB | 78.9 kB | |
withRouter.js gzip | 78.1 kB | 78.9 kB | |
Overall change | 393 kB | 398 kB |
Commit: 70f6c79
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 14.2s | 13.8s | -388ms |
nodeModulesSize | 48.9 MB | 48.9 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js gzip | 5.11 kB | 5.29 kB | |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..54d3.js gzip | 4.68 kB | 4.68 kB | ✓ |
commons.HASH.js gzip | 4.06 kB | 4.06 kB | ✓ |
de003c3a9d30..9881.js gzip | 13.7 kB | 13.7 kB | ✓ |
framework.HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 67.4 kB | 67.6 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js gzip | 4.17 kB | 4.35 kB | |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..dule.js gzip | 5.56 kB | 5.56 kB | ✓ |
de003c3a9d30..dule.js gzip | 12.5 kB | 12.5 kB | ✓ |
framework.HA..dule.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 62.1 kB | 62.2 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 4.76 kB | 4.76 kB | ✓ |
Overall change | 4.76 kB | 4.76 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js gzip | 1.33 kB | 1.33 kB | ✓ |
_error.js gzip | 4.07 kB | 4.07 kB | ✓ |
hooks.js gzip | 779 B | 779 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js gzip | 2.9 kB | 2.9 kB | ✓ |
routerDirect.js gzip | 283 B | 283 B | ✓ |
withRouter.js gzip | 282 B | 282 B | ✓ |
Overall change | 9.87 kB | 9.87 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js gzip | 757 B | 757 B | ✓ |
_error.module.js gzip | 3.06 kB | 3.06 kB | ✓ |
hooks.module.js gzip | 371 B | 371 B | ✓ |
index.module.js gzip | 212 B | 212 B | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect..dule.js gzip | 273 B | 273 B | ✓ |
withRouter.m..dule.js gzip | 272 B | 272 B | ✓ |
Overall change | 7.41 kB | 7.41 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js gzip | 61 B | 61 B | ✓ |
_buildManife..dule.js gzip | 61 B | 61 B | ✓ |
Overall change | 122 B | 122 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html gzip | 1.02 kB | 1.82 kB | |
link.html gzip | 1.03 kB | 1.83 kB | |
withRouter.html gzip | 1.01 kB | 1.81 kB | |
Overall change | 3.06 kB | 5.46 kB |
Diffs
Diff for main-HASH.js
@@ -901,6 +901,19 @@
performance.measure('Next.js-hydration', 'beforeRender', 'afterHydrate')
if (onPerfEntry) {
+ _promise['default']
+ .resolve()
+ .then(function() {
+ return (0,
+ _interopRequireWildcard2['default'])(__webpack_require__('dT2I'))
+ })
+ .then(function(mod) {
+ mod['default'](onPerfEntry)
+ })
+ ['catch'](function(err) {
+ console.error('Error measuring FID', err)
+ })
+
performance.getEntriesByName('Next.js-hydration').forEach(onPerfEntry)
performance.getEntriesByName('beforeRender').forEach(onPerfEntry)
}
@@ -944,14 +957,6 @@
return performance.clearMarks(mark)
}
)
- ;[
- 'Next.js-before-hydration',
- 'Next.js-hydration',
- 'Next.js-route-change-to-render',
- 'Next.js-render',
- ].forEach(function(measure) {
- return performance.clearMeasures(measure)
- })
}
function AppContainer(_ref5) {
@@ -1174,6 +1179,67 @@
/***/
},
+ /***/ dT2I: /***/ function(module, exports, __webpack_require__) {
+ 'use strict'
+
+ /* global hydrationMetrics */
+
+ var _Object$defineProperty = __webpack_require__('hfKm')
+
+ _Object$defineProperty(exports, '__esModule', {
+ value: true,
+ })
+
+ exports['default'] = function(onPerfEntry) {
+ function clearMeasures() {
+ ;[
+ 'Next.js-hydration',
+ 'Next.js-before-hydration',
+ 'Next.js-route-change-to-render',
+ 'Next.js-render',
+ ].forEach(function(measure) {
+ return performance.clearMeasures(measure)
+ })
+ }
+
+ hydrationMetrics.onInputDelay(function(delay, event) {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration
+ var hydrateEnd = startTime + duration
+
+ if (event.timeStamp > hydrateEnd) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd,
+ })
+ clearMeasures()
+ } else {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ }
+ }
+ })
+ }
+
+ /***/
+ },
+
/***/ yLiY: /***/ function(module, exports, __webpack_require__) {
'use strict'
Diff for main-HASH.module.js
@@ -615,6 +615,18 @@
performance.measure('Next.js-hydration', 'beforeRender', 'afterHydrate')
if (onPerfEntry) {
+ _promise.default
+ .resolve()
+ .then(() =>
+ (0, _interopRequireWildcard2.default)(__webpack_require__('dT2I'))
+ )
+ .then(mod => {
+ mod.default(onPerfEntry)
+ })
+ .catch(err => {
+ console.error('Error measuring FID', err)
+ })
+
performance.getEntriesByName('Next.js-hydration').forEach(onPerfEntry)
performance.getEntriesByName('beforeRender').forEach(onPerfEntry)
}
@@ -656,12 +668,6 @@
;['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(
mark => performance.clearMarks(mark)
)
- ;[
- 'Next.js-before-hydration',
- 'Next.js-hydration',
- 'Next.js-route-change-to-render',
- 'Next.js-render',
- ].forEach(measure => performance.clearMeasures(measure))
}
function AppContainer(_ref5) {
@@ -770,6 +776,63 @@
/***/
},
+ /***/ dT2I: /***/ function(module, exports, __webpack_require__) {
+ 'use strict'
+
+ /* global hydrationMetrics */
+
+ var _Object$defineProperty = __webpack_require__('hfKm')
+
+ _Object$defineProperty(exports, '__esModule', {
+ value: true,
+ })
+
+ exports.default = onPerfEntry => {
+ function clearMeasures() {
+ ;[
+ 'Next.js-hydration',
+ 'Next.js-before-hydration',
+ 'Next.js-route-change-to-render',
+ 'Next.js-render',
+ ].forEach(measure => performance.clearMeasures(measure))
+ }
+
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+
+ if (hydrationMeasures.length > 0) {
+ var { startTime, duration } = hydrationMeasures[0]
+ var hydrateEnd = startTime + duration
+
+ if (event.timeStamp > hydrateEnd) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd,
+ })
+ clearMeasures()
+ } else {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ }
+ }
+ })
+ }
+
+ /***/
+ },
+
/***/ yLiY: /***/ function(module, exports, __webpack_require__) {
'use strict'
Diff for index.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-6a144831c76ea3de200f.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next">Hello world 👋</div>
@@ -163,13 +265,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-7dff2e8b92a3909dd91b.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-6a144831c76ea3de200f.module.js"
defer=""
crossorigin="anonymous"
type="module"
Diff for link.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-6a144831c76ea3de200f.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next">
@@ -168,13 +270,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-7dff2e8b92a3909dd91b.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-6a144831c76ea3de200f.module.js"
defer=""
crossorigin="anonymous"
type="module"
Diff for withRouter.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-6a144831c76ea3de200f.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next"><div>I use withRouter</div></div>
@@ -163,13 +265,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-7dff2e8b92a3909dd91b.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-6a144831c76ea3de200f.module.js"
defer=""
crossorigin="anonymous"
type="module"
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 14.5s | 14s | -440ms |
nodeModulesSize | 48.9 MB | 48.9 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js gzip | 5.11 kB | 5.29 kB | |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..54d3.js gzip | 4.68 kB | 4.68 kB | ✓ |
commons.HASH.js gzip | 4.06 kB | 4.06 kB | ✓ |
de003c3a9d30..9881.js gzip | 13.7 kB | 13.7 kB | ✓ |
framework.HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 67.4 kB | 67.6 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js gzip | 4.17 kB | 4.35 kB | |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..dule.js gzip | 5.56 kB | 5.56 kB | ✓ |
de003c3a9d30..dule.js gzip | 12.5 kB | 12.5 kB | ✓ |
framework.HA..dule.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 62.1 kB | 62.2 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 4.76 kB | 4.76 kB | ✓ |
Overall change | 4.76 kB | 4.76 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js gzip | 1.33 kB | 1.33 kB | ✓ |
_error.js gzip | 4.07 kB | 4.07 kB | ✓ |
hooks.js gzip | 779 B | 779 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js gzip | 2.9 kB | 2.9 kB | ✓ |
routerDirect.js gzip | 283 B | 283 B | ✓ |
withRouter.js gzip | 282 B | 282 B | ✓ |
Overall change | 9.87 kB | 9.87 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js gzip | 757 B | 757 B | ✓ |
_error.module.js gzip | 3.06 kB | 3.06 kB | ✓ |
hooks.module.js gzip | 371 B | 371 B | ✓ |
index.module.js gzip | 212 B | 212 B | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect..dule.js gzip | 273 B | 273 B | ✓ |
withRouter.m..dule.js gzip | 272 B | 272 B | ✓ |
Overall change | 7.41 kB | 7.41 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js gzip | 61 B | 61 B | ✓ |
_buildManife..dule.js gzip | 61 B | 61 B | ✓ |
Overall change | 122 B | 122 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js gzip | 77.8 kB | 78.6 kB | |
hooks.html gzip | 1.05 kB | 1.85 kB | |
index.js gzip | 78 kB | 78.8 kB | |
link.js gzip | 80.4 kB | 81.2 kB | |
routerDirect.js gzip | 78 kB | 78.9 kB | |
withRouter.js gzip | 78.1 kB | 78.9 kB | |
Overall change | 393 kB | 398 kB |
Commit: 0d4d039
0d4d039
to
b7fe05a
Compare
Stats from current PRDefault Server Mode (Increase detected
|
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 13.1s | 13.3s | |
nodeModulesSize | 48.9 MB | 48.9 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js gzip | 5.11 kB | 5.29 kB | |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..54d3.js gzip | 4.68 kB | 4.68 kB | ✓ |
commons.HASH.js gzip | 4.06 kB | 4.06 kB | ✓ |
de003c3a9d30..9881.js gzip | 13.7 kB | 13.7 kB | ✓ |
framework.HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 67.4 kB | 67.6 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js gzip | 4.17 kB | 4.35 kB | |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..dule.js gzip | 5.56 kB | 5.56 kB | ✓ |
de003c3a9d30..dule.js gzip | 12.5 kB | 12.5 kB | ✓ |
framework.HA..dule.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 62.1 kB | 62.2 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 4.76 kB | 4.76 kB | ✓ |
Overall change | 4.76 kB | 4.76 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js gzip | 1.33 kB | 1.33 kB | ✓ |
_error.js gzip | 4.07 kB | 4.07 kB | ✓ |
hooks.js gzip | 779 B | 779 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js gzip | 2.9 kB | 2.9 kB | ✓ |
routerDirect.js gzip | 283 B | 283 B | ✓ |
withRouter.js gzip | 282 B | 282 B | ✓ |
Overall change | 9.87 kB | 9.87 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js gzip | 757 B | 757 B | ✓ |
_error.module.js gzip | 3.06 kB | 3.06 kB | ✓ |
hooks.module.js gzip | 371 B | 371 B | ✓ |
index.module.js gzip | 212 B | 212 B | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect..dule.js gzip | 273 B | 273 B | ✓ |
withRouter.m..dule.js gzip | 272 B | 272 B | ✓ |
Overall change | 7.41 kB | 7.41 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js gzip | 61 B | 61 B | ✓ |
_buildManife..dule.js gzip | 61 B | 61 B | ✓ |
Overall change | 122 B | 122 B | ✓ |
Rendered Page Sizes Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
index.html gzip | 1.02 kB | 1.82 kB | |
link.html gzip | 1.03 kB | 1.83 kB | |
withRouter.html gzip | 1.01 kB | 1.82 kB | |
Overall change | 3.06 kB | 5.47 kB |
Diffs
Diff for main-HASH.js
@@ -901,6 +901,19 @@
performance.measure('Next.js-hydration', 'beforeRender', 'afterHydrate')
if (onPerfEntry) {
+ _promise['default']
+ .resolve()
+ .then(function() {
+ return (0,
+ _interopRequireWildcard2['default'])(__webpack_require__('dT2I'))
+ })
+ .then(function(mod) {
+ mod['default'](onPerfEntry)
+ })
+ ['catch'](function(err) {
+ console.error('Error measuring FID', err)
+ })
+
performance.getEntriesByName('Next.js-hydration').forEach(onPerfEntry)
performance.getEntriesByName('beforeRender').forEach(onPerfEntry)
}
@@ -936,6 +949,11 @@
}
clearMarks()
+ ;['Next.js-route-change-to-render', 'Next.js-render'].forEach(function(
+ measure
+ ) {
+ return performance.clearMeasures(measure)
+ })
}
function clearMarks() {
@@ -944,14 +962,6 @@
return performance.clearMarks(mark)
}
)
- ;[
- 'Next.js-before-hydration',
- 'Next.js-hydration',
- 'Next.js-route-change-to-render',
- 'Next.js-render',
- ].forEach(function(measure) {
- return performance.clearMeasures(measure)
- })
}
function AppContainer(_ref5) {
@@ -1174,6 +1184,60 @@
/***/
},
+ /***/ dT2I: /***/ function(module, exports, __webpack_require__) {
+ 'use strict'
+
+ /* global hydrationMetrics */
+
+ var _Object$defineProperty = __webpack_require__('hfKm')
+
+ _Object$defineProperty(exports, '__esModule', {
+ value: true,
+ })
+
+ exports['default'] = function(onPerfEntry) {
+ hydrationMetrics.onInputDelay(function(delay, event) {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+
+ if (hydrationMeasures.length > 0) {
+ var _hydrationMeasures$ = hydrationMeasures[0],
+ startTime = _hydrationMeasures$.startTime,
+ duration = _hydrationMeasures$.duration
+ var hydrateEnd = startTime + duration
+
+ if (event.timeStamp > hydrateEnd) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd,
+ })
+ ;['Next.js-hydration', 'Next.js-before-hydration'].forEach(
+ function(measure) {
+ return performance.clearMeasures(measure)
+ }
+ )
+ } else {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ }
+ }
+ })
+ }
+
+ /***/
+ },
+
/***/ yLiY: /***/ function(module, exports, __webpack_require__) {
'use strict'
Diff for main-HASH.module.js
@@ -615,6 +615,18 @@
performance.measure('Next.js-hydration', 'beforeRender', 'afterHydrate')
if (onPerfEntry) {
+ _promise.default
+ .resolve()
+ .then(() =>
+ (0, _interopRequireWildcard2.default)(__webpack_require__('dT2I'))
+ )
+ .then(mod => {
+ mod.default(onPerfEntry)
+ })
+ .catch(err => {
+ console.error('Error measuring FID', err)
+ })
+
performance.getEntriesByName('Next.js-hydration').forEach(onPerfEntry)
performance.getEntriesByName('beforeRender').forEach(onPerfEntry)
}
@@ -650,18 +662,15 @@
}
clearMarks()
+ ;['Next.js-route-change-to-render', 'Next.js-render'].forEach(measure =>
+ performance.clearMeasures(measure)
+ )
}
function clearMarks() {
;['beforeRender', 'afterHydrate', 'afterRender', 'routeChange'].forEach(
mark => performance.clearMarks(mark)
)
- ;[
- 'Next.js-before-hydration',
- 'Next.js-hydration',
- 'Next.js-route-change-to-render',
- 'Next.js-render',
- ].forEach(measure => performance.clearMeasures(measure))
}
function AppContainer(_ref5) {
@@ -770,6 +779,56 @@
/***/
},
+ /***/ dT2I: /***/ function(module, exports, __webpack_require__) {
+ 'use strict'
+
+ /* global hydrationMetrics */
+
+ var _Object$defineProperty = __webpack_require__('hfKm')
+
+ _Object$defineProperty(exports, '__esModule', {
+ value: true,
+ })
+
+ exports.default = onPerfEntry => {
+ hydrationMetrics.onInputDelay((delay, event) => {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+
+ if (hydrationMeasures.length > 0) {
+ var { startTime, duration } = hydrationMeasures[0]
+ var hydrateEnd = startTime + duration
+
+ if (event.timeStamp > hydrateEnd) {
+ onPerfEntry({
+ name: 'first-input-delay-after-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ onPerfEntry({
+ name: 'time-to-first-input-after-hydration',
+ startTime: hydrateEnd,
+ value: event.timeStamp - hydrateEnd,
+ })
+ ;['Next.js-hydration', 'Next.js-before-hydration'].forEach(
+ measure => performance.clearMeasures(measure)
+ )
+ } else {
+ onPerfEntry({
+ name: 'first-input-delay-before-hydration',
+ startTime: event.timeStamp,
+ value: delay,
+ })
+ }
+ }
+ })
+ }
+
+ /***/
+ },
+
/***/ yLiY: /***/ function(module, exports, __webpack_require__) {
'use strict'
Diff for index.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-eabaa986b1455a8ca71c.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next">Hello world 👋</div>
@@ -163,13 +265,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-d8454a7ccf614c06d7f1.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-eabaa986b1455a8ca71c.module.js"
defer=""
crossorigin="anonymous"
type="module"
Diff for link.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-eabaa986b1455a8ca71c.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next">
@@ -168,13 +270,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-d8454a7ccf614c06d7f1.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-eabaa986b1455a8ca71c.module.js"
defer=""
crossorigin="anonymous"
type="module"
Diff for withRouter.html
@@ -45,10 +45,112 @@
/>
<link
rel="preload"
- href="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ href="/_next/static/runtime/main-eabaa986b1455a8ca71c.module.js"
as="script"
crossorigin="anonymous"
/>
+ <script>
+ ;(function fidPolyfill(addEventListener, removeEventListener) {
+ var firstInputEvent
+ var firstInputDelay
+ var firstInputTimeStamp
+ var callbacks = []
+ var listenerOpts = {
+ passive: true,
+ capture: true,
+ }
+ var startTimeStamp = +new Date()
+ var pointerup = 'pointerup'
+ var pointercancel = 'pointercancel'
+
+ function onInputDelay(callback) {
+ callbacks.push(callback)
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function recordInputDelay(delay, evt) {
+ firstInputEvent = evt
+ firstInputDelay = delay
+ firstInputTimeStamp = +new Date()
+ reportInputDelayIfRecordedAndValid()
+ }
+
+ function reportInputDelayIfRecordedAndValid() {
+ var hydrationMeasures = performance.getEntriesByName(
+ 'Next.js-hydration',
+ 'measure'
+ )
+ var firstInputStart = firstInputTimeStamp - startTimeStamp
+
+ if (
+ firstInputDelay >= 0 &&
+ firstInputDelay < firstInputStart &&
+ (hydrationMeasures.length === 0 ||
+ hydrationMeasures[0].startTime < firstInputStart)
+ ) {
+ callbacks.forEach(function(callback) {
+ callback(firstInputDelay, firstInputEvent)
+ }) // If the app is already hydrated, that means the first "post-hydration" input
+ // has been measured and listeners can be removed
+
+ if (hydrationMeasures.length > 0) {
+ eachEventType(removeEventListener)
+ callbacks = []
+ }
+ }
+ }
+
+ function onPointerDown(delay, evt) {
+ function onPointerUp() {
+ recordInputDelay(delay, evt)
+ }
+
+ function onPointerCancel() {
+ removePointerEventListeners()
+ }
+
+ function removePointerEventListeners() {
+ removeEventListener(pointerup, onPointerUp, listenerOpts)
+ removeEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ addEventListener(pointerup, onPointerUp, listenerOpts)
+ addEventListener(pointercancel, onPointerCancel, listenerOpts)
+ }
+
+ function onInput(evt) {
+ if (evt.cancelable) {
+ var isEpochTime = evt.timeStamp > 1e12
+ var now = isEpochTime ? +new Date() : performance.now()
+ var delay = now - evt.timeStamp
+
+ if (evt.type === 'pointerdown') {
+ onPointerDown(delay, evt)
+ } else {
+ recordInputDelay(delay, evt)
+ }
+ }
+ }
+
+ function eachEventType(callback) {
+ var eventTypes = [
+ 'click',
+ 'mousedown',
+ 'keydown',
+ 'touchstart',
+ 'pointerdown',
+ ]
+ eventTypes.forEach(function(eventType) {
+ callback(eventType, onInput, listenerOpts)
+ })
+ }
+
+ eachEventType(addEventListener)
+ var context = self
+ context['hydrationMetrics'] = context['hydrationMetrics'] || {}
+ context['hydrationMetrics']['onInputDelay'] = onInputDelay
+ })(addEventListener, removeEventListener)
+ </script>
</head>
<body>
<div id="__next"><div>I use withRouter</div></div>
@@ -163,13 +265,13 @@
type="module"
></script
><script
- src="/_next/static/runtime/main-20e26367cdbf3cd02b09.js"
+ src="/_next/static/runtime/main-d8454a7ccf614c06d7f1.js"
defer=""
crossorigin="anonymous"
nomodule=""
></script
><script
- src="/_next/static/runtime/main-c3e25200bb4ca113c514.module.js"
+ src="/_next/static/runtime/main-eabaa986b1455a8ca71c.module.js"
defer=""
crossorigin="anonymous"
type="module"
Serverless Mode (Increase detected ⚠️ )
General Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
buildDuration | 13.4s | 13.7s | |
nodeModulesSize | 48.9 MB | 48.9 MB |
Client Bundles (main, webpack, commons) Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.js gzip | 5.11 kB | 5.29 kB | |
webpack-HASH.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..54d3.js gzip | 4.68 kB | 4.68 kB | ✓ |
commons.HASH.js gzip | 4.06 kB | 4.06 kB | ✓ |
de003c3a9d30..9881.js gzip | 13.7 kB | 13.7 kB | ✓ |
framework.HASH.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 67.4 kB | 67.6 kB |
Client Bundles (main, webpack, commons) Modern Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
main-HASH.module.js gzip | 4.17 kB | 4.35 kB | |
webpack-HASH..dule.js gzip | 746 B | 746 B | ✓ |
4952ddcd88e7..dule.js gzip | 5.56 kB | 5.56 kB | ✓ |
de003c3a9d30..dule.js gzip | 12.5 kB | 12.5 kB | ✓ |
framework.HA..dule.js gzip | 39.1 kB | 39.1 kB | ✓ |
Overall change | 62.1 kB | 62.2 kB |
Legacy Client Bundles (polyfills)
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
polyfills-HASH.js gzip | 4.76 kB | 4.76 kB | ✓ |
Overall change | 4.76 kB | 4.76 kB | ✓ |
Client Pages
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.js gzip | 1.33 kB | 1.33 kB | ✓ |
_error.js gzip | 4.07 kB | 4.07 kB | ✓ |
hooks.js gzip | 779 B | 779 B | ✓ |
index.js gzip | 222 B | 222 B | ✓ |
link.js gzip | 2.9 kB | 2.9 kB | ✓ |
routerDirect.js gzip | 283 B | 283 B | ✓ |
withRouter.js gzip | 282 B | 282 B | ✓ |
Overall change | 9.87 kB | 9.87 kB | ✓ |
Client Pages Modern
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_app.module.js gzip | 757 B | 757 B | ✓ |
_error.module.js gzip | 3.06 kB | 3.06 kB | ✓ |
hooks.module.js gzip | 371 B | 371 B | ✓ |
index.module.js gzip | 212 B | 212 B | ✓ |
link.module.js gzip | 2.47 kB | 2.47 kB | ✓ |
routerDirect..dule.js gzip | 273 B | 273 B | ✓ |
withRouter.m..dule.js gzip | 272 B | 272 B | ✓ |
Overall change | 7.41 kB | 7.41 kB | ✓ |
Client Build Manifests
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_buildManifest.js gzip | 61 B | 61 B | ✓ |
_buildManife..dule.js gzip | 61 B | 61 B | ✓ |
Overall change | 122 B | 122 B | ✓ |
Serverless bundles Overall increase ⚠️
zeit/next.js canary | azukaru/next.js first-input-delay | Change | |
---|---|---|---|
_error.js gzip | 77.8 kB | 78.6 kB | |
hooks.html gzip | 1.05 kB | 1.85 kB | |
index.js gzip | 78 kB | 78.8 kB | |
link.js gzip | 80.4 kB | 81.2 kB | |
routerDirect.js gzip | 78 kB | 78.9 kB | |
withRouter.js gzip | 78.1 kB | 78.9 kB | |
Overall change | 393 kB | 398 kB |
Commit: b7fe05a
eebed57
to
185d55d
Compare
185d55d
to
67f85f2
Compare
PR uses a modified version of the First Input Delay polyfill that measures the first input (and delay) before hydration as well as after.
Note: #8480 needs to be merged in before this. I'll then update this PR to relay FID to user code.