Web Vitals All In One
Web Vitals All In One
Web 指标
-
Largest Contentful Paint
(LCP): measures loading performance.
To provide a good user experience, LCP should occur within 2.5 seconds of when the page first starts loading. -
First Input Delay
(FID): measures interactivity.
To provide a good user experience, pages should have a FID of 100 milliseconds or less. -
Cumulative Layout Shift
(CLS): measures visual stability.
To provide a good user experience, pages should maintain a CLS of 0.1. or less.
demo
https://www.npmjs.com/package/web-vitals
import {getCLS, getFID, getLCP} from 'web-vitals';
// onReport callback
function sendToAnalytics(metric) {
// 收集的性能指标数据
const body = JSON.stringify(metric);
if(navigator.sendBeacon) {
// Use `navigator.sendBeacon()` if available
navigator.sendBeacon('/analytics', body)
} else {
// falling back to `fetch()`.
fetch('/analytics', {body, method: 'POST', keepalive: true});
// keepalive ❓
}
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);
// once call 当指标准备好报告时
getCLS(console.log);
// multi call 报告每次更改的值
getCLS(console.log, true);
import {getCLS, getFID, getLCP} from 'web-vitals';
// 仅报告更改的增量
function logDelta({name, id, delta}) {
console.log(`${name} matching ID ${id} changed by ${delta}`);
}
getCLS(logDelta);
getFID(logDelta);
getLCP(logDelta);
PerformanceObserver
https://web.dev/vitals/
https://web.dev/i18n/zh/vitals/
https://github.com/GoogleChrome/web-vitals
// 将多个报告一起批处理
import {getCLS, getFID, getLCP} from 'web-vitals';
const queue = new Set();
function addToQueue(metric) {
queue.add(metric);
}
function flushQueue() {
if (queue.size > 0) {
// Replace with whatever serialization method you prefer.
// Note: JSON.stringify will likely include more data than you need.
const body = JSON.stringify([...queue]);
if(navigator.sendBeacon) {
// Use `navigator.sendBeacon()` if available
navigator.sendBeacon('/analytics', body)
} else {
// falling back to `fetch()`.
fetch('/analytics', {body, method: 'POST', keepalive: true});
// keepalive ❓
}
// 上报完成,清零
queue.clear();
}
}
getCLS(addToQueue);
getFID(addToQueue);
getLCP(addToQueue);
// Report all available metrics whenever the page is backgrounded or unloaded.
// 当页面后台或卸载时报告所有可用指标。
addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
flushQueue();
}
});
// NOTE: Safari does not reliably fire the `visibilitychange` event when the page is being unloaded.
// If Safari support is needed, you should also flush the queue in the `pagehide` event.
addEventListener('pagehide', flushQueue);
Note: see the Page Lifecycle
guide for an explanation of why visibilitychange
and pagehide
are recommended over events like beforeunload
and unload
.
https://developers.google.com/web/updates/2018/07/page-lifecycle-api#legacy-lifecycle-apis-to-avoid
cdn
<!-- Append the `?module` param to load the module version of `web-vitals` -->
<script type="module">
import {getCLS, getFID, getLCP} from 'https://unpkg.com/web-vitals?module';
getCLS(console.log);
getFID(console.log);
getLCP(console.log);
</script>
<script>
(function() {
var script = document.createElement('script');
script.src = 'https://unpkg.com/web-vitals/dist/web-vitals.iife.js';
script.onload = function() {
// When loading `web-vitals` using a classic script, all the public
// methods can be found on the `webVitals` global namespace.
webVitals.getCLS(console.log);
webVitals.getFID(console.log);
webVitals.getLCP(console.log);
}
document.head.appendChild(script);
}())
</script>
Metric
interface Metric {
// The name of the metric (in acronym form).
name: 'CLS' | 'FCP' | 'FID' | 'LCP' | 'TTFB';
// The current value of the metric.
value: number;
// The delta between the current value and the last-reported value.
// On the first report, `delta` and `value` will always be the same.
delta: number;
// A unique ID representing this particular metric that's specific to the
// current page. This ID can be used by an analytics tool to dedupe
// multiple values sent for the same metric, or to group multiple deltas
// together and calculate a total.
id: string;
// Any performance entries used in the metric value calculation.
// Note, entries will be added to the array as the value changes.
entries: (PerformanceEntry | FirstInputPolyfillEntry | NavigationTimingPolyfillEntry)[];
}
interface ReportHandler {
(metric: Metric): void;
}
// Event Timing API
type FirstInputPolyfillEntry = Omit<PerformanceEventTiming, 'processingEnd' | 'toJSON'>
interface FirstInputPolyfillCallback {
(entry: FirstInputPolyfillEntry): void;
}
// Navigation Timing API Level 2
export type NavigationTimingPolyfillEntry = Omit<PerformanceNavigationTiming,
'initiatorType' | 'nextHopProtocol' | 'redirectCount' | 'transferSize' |
'encodedBodySize' | 'decodedBodySize' | 'toJSON'>
interface WebVitalsGlobal {
firstInputPolyfill: (onFirstInput: FirstInputPolyfillCallback) => void;
resetFirstInputPolyfill: () => void;
firstHiddenTime: number;
}
interface WebVitalsGlobal {
firstInputPolyfill: (onFirstInput: FirstInputPolyfillCallback) => void;
resetFirstInputPolyfill: () => void;
firstHiddenTime: number;
}
type getCLS = (onReport: ReportHandler, reportAllChanges?: boolean) => void
type getFCP = (onReport: ReportHandler, reportAllChanges?: boolean) => void
type getFID = (onReport: ReportHandler, reportAllChanges?: boolean) => void
type getLCP = (onReport: ReportHandler, reportAllChanges?: boolean) => void
type getTTFB = (onReport: ReportHandler, reportAllChanges?: boolean) => void
import {getTTFB} from 'web-vitals';
getTTFB((metric) => {
// Calculate the request time by subtracting from TTFB
// everything that happened prior to the request starting.
const requestTime = metric.value - metric.entries[0].requestStart;
console.log('Request time:', requestTime);
});
DOM 高分辨率时间戳 / DOM 高精度时间戳
https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
automation test framework
Next-gen browser and mobile automation test framework for Node.js
$ npm install --save-dev @wdio/cli
$ npx wdio config --yes
$ npx wdio run
https://github.com/webdriverio/webdriverio
Google Analytics
analytics.js
import {getCLS, getFID, getLCP} from 'web-vitals';
function sendToGoogleAnalytics({name, delta, id}) {
// Assumes the global `ga()` function exists, see:
// https://developers.google.com/analytics/devguides/collection/analyticsjs
ga('send', 'event', {
eventCategory: 'Web Vitals',
eventAction: name,
// The `id` value will be unique to the current page load. When sending
// multiple values from the same page (e.g. for CLS), Google Analytics can
// compute a total by grouping on this ID (note: requires `eventLabel` to
// be a dimension in your report).
eventLabel: id,
// Google Analytics metrics must be integers, so the value is rounded.
// For CLS the value is first multiplied by 1000 for greater precision
// (note: increase the multiplier for greater precision if needed).
eventValue: Math.round(name === 'CLS' ? delta * 1000 : delta),
// Use a non-interaction event to avoid affecting bounce rate.
nonInteraction: true,
// Use `sendBeacon()` if the browser supports it.
transport: 'beacon',
// OPTIONAL: any additional params or debug info here.
// See: https://web.dev/debug-web-vitals-in-the-field/
// dimension1: '...',
// dimension2: '...',
// ...
});
}
getCLS(sendToGoogleAnalytics);
getFID(sendToGoogleAnalytics);
getLCP(sendToGoogleAnalytics);
gtag.js (Universal Analytics)
import {getCLS, getFID, getLCP} from 'web-vitals';
function sendToGoogleAnalytics({name, delta, id}) {
// Assumes the global `gtag()` function exists, see:
// https://developers.google.com/analytics/devguides/collection/gtagjs
gtag('event', name, {
event_category: 'Web Vitals',
// The `id` value will be unique to the current page load. When sending
// multiple values from the same page (e.g. for CLS), Google Analytics can
// compute a total by grouping on this ID (note: requires `eventLabel` to
// be a dimension in your report).
event_label: id,
// Google Analytics metrics must be integers, so the value is rounded.
// For CLS the value is first multiplied by 1000 for greater precision
// (note: increase the multiplier for greater precision if needed).
value: Math.round(name === 'CLS' ? delta * 1000 : delta),
// Use a non-interaction event to avoid affecting bounce rate.
non_interaction: true,
// OPTIONAL: any additional params or debug info here.
// See: https://web.dev/debug-web-vitals-in-the-field/
// metric_rating: 'good' | 'ni' | 'poor',
// debug_info: '...',
// ...
});
}
getCLS(sendToGoogleAnalytics);
getFID(sendToGoogleAnalytics);
getLCP(sendToGoogleAnalytics);
https://developers.google.com/analytics/devguides/reporting/core/v4
https://developers.google.com/analytics/devguides/reporting/core/v4/quickstart/web-js
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello Analytics Reporting API V4</title>
<meta name="google-signin-client_id" content="<REPLACE_WITH_CLIENT_ID>">
<meta name="google-signin-scope" content="https://www.googleapis.com/auth/analytics.readonly">
</head>
<body>
<h1>Hello Analytics Reporting API V4</h1>
<!-- The Sign-in button. This will run `queryReports()` on success. -->
<p class="g-signin2" data-onsuccess="queryReports"></p>
<!-- The API response will be printed here. -->
<textarea cols="80" rows="20" id="query-output"></textarea>
<script>
// Replace with your view ID.
var VIEW_ID = '<REPLACE_WITH_VIEW_ID>';
// Query the API and print the results to the page.
function queryReports() {
gapi.client.request({
path: '/v4/reports:batchGet',
root: 'https://analyticsreporting.googleapis.com/',
method: 'POST',
body: {
reportRequests: [
{
viewId: VIEW_ID,
dateRanges: [
{
startDate: '7daysAgo',
endDate: 'today'
}
],
metrics: [
{
expression: 'ga:sessions'
}
]
}
]
}
}).then(displayResults, console.error.bind(console));
}
function displayResults(response) {
var formattedJson = JSON.stringify(response.result, null, 2);
document.getElementById('query-output').value = formattedJson;
}
</script>
<!-- Load the JavaScript API client and Sign-in library. -->
<script src="https://apis.google.com/js/client:platform.js"></script>
</body>
</html>
Google Analytics Reporting API
https://github.com/GoogleChromeLabs/web-vitals-report
https://developers.google.com/analytics/devguides/reporting
refs
Web 性能监控
https://github.com/web-fullstack/web-vitals
https://www.cnblogs.com/xgqfrms/p/16514500.html
©xgqfrms 2012-2020
www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!
原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/16514500.html
未经授权禁止转载,违者必究!