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 */
|
||||
import {
|
||||
getCLS,
|
||||
getFCP,
|
||||
getFID,
|
||||
getLCP,
|
||||
getTTFB,
|
||||
onCLS,
|
||||
onFCP,
|
||||
onFID,
|
||||
onINP,
|
||||
onLCP,
|
||||
onTTFB,
|
||||
Metric,
|
||||
ReportHandler,
|
||||
ReportCallback,
|
||||
} from 'next/dist/compiled/web-vitals'
|
||||
|
||||
const initialHref = location.href
|
||||
let isRegistered = false
|
||||
let userReportHandler: ReportHandler | undefined
|
||||
let userReportHandler: ReportCallback | undefined
|
||||
|
||||
function onReport(metric: Metric): void {
|
||||
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:
|
||||
userReportHandler = onPerfEntry
|
||||
|
||||
|
@ -81,9 +82,10 @@ export default (onPerfEntry?: ReportHandler): void => {
|
|||
}
|
||||
isRegistered = true
|
||||
|
||||
getCLS(onReport)
|
||||
getFID(onReport)
|
||||
getFCP(onReport)
|
||||
getLCP(onReport)
|
||||
getTTFB(onReport)
|
||||
onCLS(onReport)
|
||||
onFID(onReport)
|
||||
onFCP(onReport)
|
||||
onLCP(onReport)
|
||||
onTTFB(onReport)
|
||||
onINP(onReport)
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -270,7 +270,7 @@
|
|||
"uuid": "8.3.2",
|
||||
"vm-browserify": "1.1.2",
|
||||
"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-sources3": "npm:webpack-sources@3.2.3",
|
||||
"webpack4": "npm:webpack@4.44.1",
|
||||
|
|
|
@ -47,7 +47,7 @@ export type NextWebVitalsMetric = {
|
|||
} & (
|
||||
| {
|
||||
label: 'web-vital'
|
||||
name: 'FCP' | 'LCP' | 'CLS' | 'FID' | 'TTFB'
|
||||
name: 'FCP' | 'LCP' | 'CLS' | 'FID' | 'TTFB' | 'INP'
|
||||
}
|
||||
| {
|
||||
label: 'custom'
|
||||
|
|
|
@ -565,7 +565,7 @@ importers:
|
|||
uuid: 8.3.2
|
||||
vm-browserify: 1.1.2
|
||||
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-sources3: npm:webpack-sources@3.2.3
|
||||
webpack4: npm:webpack@4.44.1
|
||||
|
@ -753,7 +753,7 @@ importers:
|
|||
uuid: 8.3.2
|
||||
vm-browserify: 1.1.2
|
||||
watchpack: 2.4.0
|
||||
web-vitals: 2.1.0
|
||||
web-vitals: 3.0.0-beta.2
|
||||
webpack-sources1: /webpack-sources/1.4.3
|
||||
webpack-sources3: /webpack-sources/3.2.3
|
||||
webpack4: /webpack/4.44.1
|
||||
|
@ -1058,7 +1058,7 @@ packages:
|
|||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.17.0
|
||||
'@babel/compat-data': 7.17.10
|
||||
'@babel/core': 7.17.5
|
||||
'@babel/helper-validator-option': 7.16.7
|
||||
browserslist: 4.20.2
|
||||
|
@ -1071,7 +1071,7 @@ packages:
|
|||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
dependencies:
|
||||
'@babel/compat-data': 7.17.0
|
||||
'@babel/compat-data': 7.17.10
|
||||
'@babel/core': 7.18.0
|
||||
'@babel/helper-validator-option': 7.16.7
|
||||
browserslist: 4.20.2
|
||||
|
@ -8823,7 +8823,7 @@ packages:
|
|||
mississippi: 3.0.0
|
||||
mkdirp: 0.5.5
|
||||
move-concurrently: 1.0.1
|
||||
promise-inflight: 1.0.1
|
||||
promise-inflight: 1.0.1_bluebird@3.7.2
|
||||
rimraf: 2.7.1
|
||||
ssri: 6.0.1
|
||||
unique-filename: 1.1.1
|
||||
|
@ -15129,7 +15129,7 @@ packages:
|
|||
dev: true
|
||||
|
||||
/json-schema/0.2.3:
|
||||
resolution: {integrity: sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=}
|
||||
resolution: {integrity: sha512-a3xHnILGMtk+hDOqNwHzF6e2fNbiMrXZvxKQiEv2MlgQP+pjIOzqAmKYD2mDpXYE/44M7g+n9p2bKkYWDUcXCQ==}
|
||||
dev: true
|
||||
|
||||
/json-stable-stringify-without-jsonify/1.0.1:
|
||||
|
@ -19125,6 +19125,17 @@ packages:
|
|||
optional: 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:
|
||||
resolution: {integrity: sha1-36lpQ+qcEh/KTem1hoyznTRy4Fc=}
|
||||
dev: true
|
||||
|
@ -23107,8 +23118,8 @@ packages:
|
|||
resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}
|
||||
dev: true
|
||||
|
||||
/web-vitals/2.1.0:
|
||||
resolution: {integrity: sha512-npEyJP8jHf3J71t1tRTEtz9FeKp8H2udWJUUq5ykfPhhstr//TUxiYhIEzLNwk4zv2ybAilMn7v7N6Mxmuitmg==}
|
||||
/web-vitals/3.0.0-beta.2:
|
||||
resolution: {integrity: sha512-W9OALsWK4RkA5GWvLhsfszy+Q29WJBB27Dnucc3eYP6/0kz1XsfMgm+4au9X/KjXMIo92ZRU1fWBaSdNsaVjJg==}
|
||||
dev: true
|
||||
|
||||
/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 () => {
|
||||
// Below comment will be used for replacing exported report method with hook based one.
|
||||
return (
|
||||
<div>
|
||||
<h1>Foo!</h1>
|
||||
<h2>bar!</h2>
|
||||
<button onClick={toggleText}>Click</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -108,4 +108,18 @@ function runTest() {
|
|||
expect(stdout).toMatch('Next.js Analytics')
|
||||
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