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:
Islam Shehata 2023-03-24 12:13:50 +02:00 committed by GitHub
parent be14eb1373
commit b39a03f4e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 127 additions and 1 deletions

View file

@ -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"

View 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
View file

@ -0,0 +1 @@
export * from './dist/client/web-vitals'

View file

@ -0,0 +1 @@
module.exports = require('./dist/client/web-vitals')

View 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}
</>
)
}

View file

@ -0,0 +1,10 @@
import Reporter from './reporter'
export default function component() {
return (
<>
<h1>Test</h1>
<Reporter />
</>
)
}

View 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)
}

View 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/)
})
})