add method to measure Interaction to Next Paint (INP) (#36490)
This commit lets users measure their Interaction to Next Paint [INP](https://web.dev/inp/) web vital. Note that the `web-vitals` package is beta to denote that INP is an experimental metric, the code is stable and v3 is backwards compatible. `web-vitals` CHANGELOG for v3: - [BREAKING] Report TTFB after a bfcache restore - [BREAKING] Only include last LCP entry in metric entries - Add support for the new INP metric - Rename getXXX() functions to onXXX() - Add a navigationType property to the Metric object See https://github.com/GoogleChrome/web-vitals/blob/next/CHANGELOG.md Upgraded `playwright-chromium` from `1.14.1` to `1.17.2` because the Events Timing API used to measure INP is only available in Chromium >= v98. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint`
This commit is contained in:
parent
42f838e156
commit
dc36199b22
7 changed files with 60 additions and 24 deletions
|
@ -1,17 +1,18 @@
|
||||||
/* global location */
|
/* global location */
|
||||||
import {
|
import {
|
||||||
getCLS,
|
onCLS,
|
||||||
getFCP,
|
onFCP,
|
||||||
getFID,
|
onFID,
|
||||||
getLCP,
|
onINP,
|
||||||
getTTFB,
|
onLCP,
|
||||||
|
onTTFB,
|
||||||
Metric,
|
Metric,
|
||||||
ReportHandler,
|
ReportCallback,
|
||||||
} from 'next/dist/compiled/web-vitals'
|
} from 'next/dist/compiled/web-vitals'
|
||||||
|
|
||||||
const initialHref = location.href
|
const initialHref = location.href
|
||||||
let isRegistered = false
|
let isRegistered = false
|
||||||
let userReportHandler: ReportHandler | undefined
|
let userReportHandler: ReportCallback | undefined
|
||||||
|
|
||||||
function onReport(metric: Metric): void {
|
function onReport(metric: Metric): void {
|
||||||
if (userReportHandler) {
|
if (userReportHandler) {
|
||||||
|
@ -71,7 +72,7 @@ function onReport(metric: Metric): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (onPerfEntry?: ReportHandler): void => {
|
export default (onPerfEntry?: ReportCallback): void => {
|
||||||
// Update function if it changes:
|
// Update function if it changes:
|
||||||
userReportHandler = onPerfEntry
|
userReportHandler = onPerfEntry
|
||||||
|
|
||||||
|
@ -81,9 +82,10 @@ export default (onPerfEntry?: ReportHandler): void => {
|
||||||
}
|
}
|
||||||
isRegistered = true
|
isRegistered = true
|
||||||
|
|
||||||
getCLS(onReport)
|
onCLS(onReport)
|
||||||
getFID(onReport)
|
onFID(onReport)
|
||||||
getFCP(onReport)
|
onFCP(onReport)
|
||||||
getLCP(onReport)
|
onLCP(onReport)
|
||||||
getTTFB(onReport)
|
onTTFB(onReport)
|
||||||
|
onINP(onReport)
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -270,7 +270,7 @@
|
||||||
"uuid": "8.3.2",
|
"uuid": "8.3.2",
|
||||||
"vm-browserify": "1.1.2",
|
"vm-browserify": "1.1.2",
|
||||||
"watchpack": "2.4.0",
|
"watchpack": "2.4.0",
|
||||||
"web-vitals": "2.1.0",
|
"web-vitals": "3.0.0-beta.2",
|
||||||
"webpack-sources1": "npm:webpack-sources@1.4.3",
|
"webpack-sources1": "npm:webpack-sources@1.4.3",
|
||||||
"webpack-sources3": "npm:webpack-sources@3.2.3",
|
"webpack-sources3": "npm:webpack-sources@3.2.3",
|
||||||
"webpack4": "npm:webpack@4.44.1",
|
"webpack4": "npm:webpack@4.44.1",
|
||||||
|
|
|
@ -47,7 +47,7 @@ export type NextWebVitalsMetric = {
|
||||||
} & (
|
} & (
|
||||||
| {
|
| {
|
||||||
label: 'web-vital'
|
label: 'web-vital'
|
||||||
name: 'FCP' | 'LCP' | 'CLS' | 'FID' | 'TTFB'
|
name: 'FCP' | 'LCP' | 'CLS' | 'FID' | 'TTFB' | 'INP'
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
label: 'custom'
|
label: 'custom'
|
||||||
|
|
|
@ -565,7 +565,7 @@ importers:
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
vm-browserify: 1.1.2
|
vm-browserify: 1.1.2
|
||||||
watchpack: 2.4.0
|
watchpack: 2.4.0
|
||||||
web-vitals: 2.1.0
|
web-vitals: 3.0.0-beta.2
|
||||||
webpack-sources1: npm:webpack-sources@1.4.3
|
webpack-sources1: npm:webpack-sources@1.4.3
|
||||||
webpack-sources3: npm:webpack-sources@3.2.3
|
webpack-sources3: npm:webpack-sources@3.2.3
|
||||||
webpack4: npm:webpack@4.44.1
|
webpack4: npm:webpack@4.44.1
|
||||||
|
@ -753,7 +753,7 @@ importers:
|
||||||
uuid: 8.3.2
|
uuid: 8.3.2
|
||||||
vm-browserify: 1.1.2
|
vm-browserify: 1.1.2
|
||||||
watchpack: 2.4.0
|
watchpack: 2.4.0
|
||||||
web-vitals: 2.1.0
|
web-vitals: 3.0.0-beta.2
|
||||||
webpack-sources1: /webpack-sources/1.4.3
|
webpack-sources1: /webpack-sources/1.4.3
|
||||||
webpack-sources3: /webpack-sources/3.2.3
|
webpack-sources3: /webpack-sources/3.2.3
|
||||||
webpack4: /webpack/4.44.1
|
webpack4: /webpack/4.44.1
|
||||||
|
@ -1058,7 +1058,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0
|
'@babel/core': ^7.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/compat-data': 7.17.0
|
'@babel/compat-data': 7.17.10
|
||||||
'@babel/core': 7.17.5
|
'@babel/core': 7.17.5
|
||||||
'@babel/helper-validator-option': 7.16.7
|
'@babel/helper-validator-option': 7.16.7
|
||||||
browserslist: 4.20.2
|
browserslist: 4.20.2
|
||||||
|
@ -1071,7 +1071,7 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0
|
'@babel/core': ^7.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/compat-data': 7.17.0
|
'@babel/compat-data': 7.17.10
|
||||||
'@babel/core': 7.18.0
|
'@babel/core': 7.18.0
|
||||||
'@babel/helper-validator-option': 7.16.7
|
'@babel/helper-validator-option': 7.16.7
|
||||||
browserslist: 4.20.2
|
browserslist: 4.20.2
|
||||||
|
@ -8823,7 +8823,7 @@ packages:
|
||||||
mississippi: 3.0.0
|
mississippi: 3.0.0
|
||||||
mkdirp: 0.5.5
|
mkdirp: 0.5.5
|
||||||
move-concurrently: 1.0.1
|
move-concurrently: 1.0.1
|
||||||
promise-inflight: 1.0.1
|
promise-inflight: 1.0.1_bluebird@3.7.2
|
||||||
rimraf: 2.7.1
|
rimraf: 2.7.1
|
||||||
ssri: 6.0.1
|
ssri: 6.0.1
|
||||||
unique-filename: 1.1.1
|
unique-filename: 1.1.1
|
||||||
|
@ -15129,7 +15129,7 @@ packages:
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/json-schema/0.2.3:
|
/json-schema/0.2.3:
|
||||||
resolution: {integrity: sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=}
|
resolution: {integrity: sha512-a3xHnILGMtk+hDOqNwHzF6e2fNbiMrXZvxKQiEv2MlgQP+pjIOzqAmKYD2mDpXYE/44M7g+n9p2bKkYWDUcXCQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/json-stable-stringify-without-jsonify/1.0.1:
|
/json-stable-stringify-without-jsonify/1.0.1:
|
||||||
|
@ -19125,6 +19125,17 @@ packages:
|
||||||
optional: true
|
optional: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/promise-inflight/1.0.1_bluebird@3.7.2:
|
||||||
|
resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
|
||||||
|
peerDependencies:
|
||||||
|
bluebird: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
bluebird:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
bluebird: 3.7.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/promise-polyfill/6.1.0:
|
/promise-polyfill/6.1.0:
|
||||||
resolution: {integrity: sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=}
|
resolution: {integrity: sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -23107,8 +23118,8 @@ packages:
|
||||||
resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}
|
resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/web-vitals/2.1.0:
|
/web-vitals/3.0.0-beta.2:
|
||||||
resolution: {integrity: sha512-npEyJP8jHf3J71t1tRTEtz9FeKp8H2udWJUUq5ykfPhhstr//TUxiYhIEzLNwk4zv2ybAilMn7v7N6Mxmuitmg==}
|
resolution: {integrity: sha512-W9OALsWK4RkA5GWvLhsfszy+Q29WJBB27Dnucc3eYP6/0kz1XsfMgm+4au9X/KjXMIo92ZRU1fWBaSdNsaVjJg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/webidl-conversions/3.0.1:
|
/webidl-conversions/3.0.1:
|
||||||
|
|
|
@ -16,12 +16,21 @@ if (typeof navigator !== 'undefined') {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleText(e) {
|
||||||
|
const startTime = performance.now()
|
||||||
|
while (performance.now() < startTime + 100) {
|
||||||
|
// busy waiting
|
||||||
|
}
|
||||||
|
e.target.textContent = e.target.textContent === 'Click' ? 'Press' : 'Click'
|
||||||
|
}
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
// Below comment will be used for replacing exported report method with hook based one.
|
// Below comment will be used for replacing exported report method with hook based one.
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>Foo!</h1>
|
<h1>Foo!</h1>
|
||||||
<h2>bar!</h2>
|
<h2>bar!</h2>
|
||||||
|
<button onClick={toggleText}>Click</button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,4 +108,18 @@ function runTest() {
|
||||||
expect(stdout).toMatch('Next.js Analytics')
|
expect(stdout).toMatch('Next.js Analytics')
|
||||||
await browser.close()
|
await browser.close()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('reports INP metric', async () => {
|
||||||
|
const browser = await webdriver(appPort, '/')
|
||||||
|
await browser.elementByCss('button').click()
|
||||||
|
await browser.waitForCondition(
|
||||||
|
'document.querySelector("button").textContent === "Press"'
|
||||||
|
)
|
||||||
|
// INP metric is only reported on pagehide or visibilitychange event, so refresh the page
|
||||||
|
await browser.refresh()
|
||||||
|
const INP = parseInt(await browser.eval('localStorage.getItem("INP")'), 10)
|
||||||
|
// We introduced a delay of 100ms, so INP duration should be >= 100
|
||||||
|
expect(INP).toBeGreaterThanOrEqual(100)
|
||||||
|
await browser.close()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue