add useReportWebVitals that makes use of web-vitals package (#47319)
### What? introduce a new hook `useReportWebVitals` that would register a function to handle web-vitals metrics. ### Why? next.js users who use [Axiom](https://axiom.co) has been [asking](https://github.com/axiomhq/next-axiom/issues/109) for this feature for nextjs 13, as it was working for nextjs 12.
This commit is contained in:
parent
be14eb1373
commit
b39a03f4e3
8 changed files with 127 additions and 1 deletions
|
@ -57,7 +57,9 @@
|
|||
"navigation.d.ts",
|
||||
"headers.js",
|
||||
"headers.d.ts",
|
||||
"navigation-types"
|
||||
"navigation-types",
|
||||
"web-vitals.js",
|
||||
"web-vitals.d.ts"
|
||||
],
|
||||
"bin": {
|
||||
"next": "./dist/bin/next"
|
||||
|
|
23
packages/next/src/client/web-vitals.ts
Normal file
23
packages/next/src/client/web-vitals.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { useEffect } from 'react'
|
||||
import {
|
||||
onLCP,
|
||||
onFID,
|
||||
onCLS,
|
||||
onINP,
|
||||
onFCP,
|
||||
onTTFB,
|
||||
Metric,
|
||||
} from 'next/dist/compiled/web-vitals'
|
||||
|
||||
export function useReportWebVitals(
|
||||
reportWebVitalsFn: (metric: Metric) => void
|
||||
) {
|
||||
useEffect(() => {
|
||||
onCLS(reportWebVitalsFn)
|
||||
onFID(reportWebVitalsFn)
|
||||
onLCP(reportWebVitalsFn)
|
||||
onINP(reportWebVitalsFn)
|
||||
onFCP(reportWebVitalsFn)
|
||||
onTTFB(reportWebVitalsFn)
|
||||
}, [reportWebVitalsFn])
|
||||
}
|
1
packages/next/web-vitals.d.ts
vendored
Normal file
1
packages/next/web-vitals.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export * from './dist/client/web-vitals'
|
1
packages/next/web-vitals.js
Normal file
1
packages/next/web-vitals.js
Normal file
|
@ -0,0 +1 @@
|
|||
module.exports = require('./dist/client/web-vitals')
|
19
test/e2e/app-dir/app/app/report-web-vitals/layout.js
Normal file
19
test/e2e/app-dir/app/app/report-web-vitals/layout.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export default function ClientNestedLayout({ children }) {
|
||||
const [count, setCount] = useState(0)
|
||||
useEffect(() => {
|
||||
setCount(1)
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<h1>Client Nested. Count: {count}</h1>
|
||||
<button id="btn" onClick={() => setCount(count + 1)}>
|
||||
{count}
|
||||
</button>
|
||||
{children}
|
||||
</>
|
||||
)
|
||||
}
|
10
test/e2e/app-dir/app/app/report-web-vitals/page.js
Normal file
10
test/e2e/app-dir/app/app/report-web-vitals/page.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import Reporter from './reporter'
|
||||
|
||||
export default function component() {
|
||||
return (
|
||||
<>
|
||||
<h1>Test</h1>
|
||||
<Reporter />
|
||||
</>
|
||||
)
|
||||
}
|
17
test/e2e/app-dir/app/app/report-web-vitals/reporter.js
Normal file
17
test/e2e/app-dir/app/app/report-web-vitals/reporter.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
'use client'
|
||||
import { useReportWebVitals } from 'next/web-vitals'
|
||||
|
||||
const report = (metric) => {
|
||||
const blob = new Blob([new URLSearchParams(metric).toString()])
|
||||
const vitalsUrl = 'https://example.vercel.sh/vitals'
|
||||
fetch(vitalsUrl, {
|
||||
body: blob,
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
keepalive: true,
|
||||
})
|
||||
}
|
||||
|
||||
export default function Reporter() {
|
||||
useReportWebVitals(report)
|
||||
}
|
53
test/e2e/app-dir/app/useReportWebVitals.test.ts
Normal file
53
test/e2e/app-dir/app/useReportWebVitals.test.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { createNext } from 'e2e-utils'
|
||||
import { NextInstance } from 'test/lib/next-modes/base'
|
||||
import { check } from 'next-test-utils'
|
||||
|
||||
describe('useReportWebVitals hook', () => {
|
||||
let next: NextInstance
|
||||
|
||||
beforeAll(async () => {
|
||||
next = await createNext({
|
||||
files: __dirname,
|
||||
dependencies: {
|
||||
swr: '2.0.0-rc.0',
|
||||
react: 'latest',
|
||||
'react-dom': 'latest',
|
||||
sass: 'latest',
|
||||
},
|
||||
skipStart: true,
|
||||
env: {},
|
||||
})
|
||||
|
||||
await next.start()
|
||||
})
|
||||
afterAll(() => next.destroy())
|
||||
|
||||
// Analytics events are only sent in production
|
||||
it('should send web-vitals to vercel-insights', async () => {
|
||||
let countEvents = false
|
||||
let eventsCount = 0
|
||||
const browser = await next.browser('/report-web-vitals', {
|
||||
beforePageLoad: (page) => {
|
||||
page.route('https://example.vercel.sh/vitals', (route) => {
|
||||
if (countEvents) {
|
||||
eventsCount += 1
|
||||
}
|
||||
|
||||
route.fulfill()
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
// Start counting analytics events
|
||||
countEvents = true
|
||||
|
||||
// Refresh will trigger CLS and LCP. When page loads FCP and TTFB will trigger:
|
||||
await browser.refresh()
|
||||
|
||||
// After interaction LCP and FID will trigger
|
||||
await browser.elementById('btn').click()
|
||||
|
||||
// Make sure all registered events in performance-relayer has fired
|
||||
await check(() => eventsCount, /6/)
|
||||
})
|
||||
})
|
Loading…
Reference in a new issue