深入探讨Web性能API,从传统的时间测量到以用户为中心的现代指标(如Core Web Vitals),以及如何连接它们以获得全面的性能视图。
超越时钟:将Web性能API连接到真实用户体验
在数字经济中,速度不仅仅是一个功能;它是用户体验的基础。一个缓慢的网站会导致用户感到沮丧,跳出率升高,并直接影响收入。多年来,开发人员一直依赖于诸如window.onload
之类的时间测量指标来衡量性能。但是,快速的加载时间真的等同于快乐的用户吗?答案通常是否定的。
一个页面可以在不到一秒钟的时间内完成所有技术资源的加载,但对于试图与之交互的真人来说,却感觉迟缓且无法使用。这种脱节突出了Web开发中的一个关键演变:从测量技术时间到量化人类体验的转变。现代Web性能是两种观点的故事:Web性能API提供的粒度化、底层数据和诸如Google的Core Web Vitals之类的高级、以用户为中心的指标。
本综合指南将弥合这一差距。我们将探索作为我们诊断工具的强大的Web性能API套件。然后,我们将深入研究现代用户体验指标,这些指标告诉我们性能的*感受*。最重要的是,我们将连接各个点,向您展示如何使用底层时间数据来诊断和修复全球用户的糟糕用户体验的根本原因。
基础:理解Web性能API
Web性能API是一组标准化的浏览器接口,使开发人员可以访问与Web页面的导航和呈现相关的高度详细和准确的时间数据。它们是性能测量的基石,使我们能够超越简单的秒表,并了解网络请求、解析和呈现的复杂过程。
导航计时API:页面的旅程
导航计时API提供了加载主文档所需时间的详细分解。它捕获了从用户发起导航(如单击链接)到页面完全加载的那一刻的里程碑。这是我们对页面加载过程的第一个也是最基本的视图。
您可以使用一个简单的JavaScript调用来访问此数据:
const navigationEntry = performance.getEntriesByType('navigation')[0];
console.log(navigationEntry.toJSON());
这将返回一个充满时间戳的对象。一些关键属性包括:
- fetchStart: 浏览器开始获取文档的时间。
- responseStart: 浏览器从服务器接收到响应的第一个字节的时间。
fetchStart
和responseStart
之间的时间通常称为首字节时间(TTFB)。 - domContentLoadedEventEnd: 初始HTML文档已完全加载和解析的时间,无需等待样式表、图像和子帧完成加载。
- loadEventEnd: 页面上的所有资源(包括图像、CSS等)已完全加载的时间。
长期以来,loadEventEnd
一直是黄金标准。但是,它的局限性很严重:它没有说明用户何时*看到*有意义的内容,或者他们何时可以与页面*交互*。这是一个技术里程碑,而不是一个人类里程碑。
资源计时API:解构组件
Web页面很少是单个文件。它是HTML、CSS、JavaScript、图像、字体和API调用的集合。资源计时API允许您检查每个单独资源的網絡定时。
这对于识别瓶颈非常强大。来自另一个大洲的内容分发网络(CDN)上的大型、未优化的英雄图像是否减慢了初始渲染速度?第三方分析脚本是否阻塞了主线程?资源计时可以帮助您回答这些问题。
您可以像这样获取所有资源的列表:
const resourceEntries = performance.getEntriesByType('resource');
resourceEntries.forEach(resource => {
if (resource.duration > 200) { // Find resources that took longer than 200ms
console.log(`Slow resource: ${resource.name}, Duration: ${resource.duration}ms`);
}
});
关键属性包括name
(资源的URL),initiatorType
(导致资源被加载的原因,例如“img”,“script”)和duration
(获取它所花费的总时间)。
用户计时API:测量应用程序的逻辑
有时,性能瓶颈不在于加载资产,而在于客户端代码本身。在从API接收到数据后,您的单页应用程序(SPA)需要多长时间来渲染一个复杂的组件?用户计时API允许您创建自定义的、特定于应用程序的测量。
它使用两种主要方法:
- performance.mark(name): 在性能缓冲区中创建一个命名的时间戳。
- performance.measure(name, startMark, endMark): 计算两个标记之间的持续时间,并创建一个命名的测量。
示例: 测量产品列表组件的渲染时间。
// When you start fetching data
performance.mark('product-list-fetch-start');
fetch('/api/products')
.then(response => response.json())
.then(data => {
// After fetching, before rendering
performance.mark('product-list-render-start');
renderProductList(data);
// Immediately after rendering is complete
performance.mark('product-list-render-end');
// Create a measure
performance.measure(
'Product List Render Time',
'product-list-render-start',
'product-list-render-end'
);
});
这使您可以精确控制以测量对用户工作流程至关重要的应用程序部分。
PerformanceObserver:现代、高效的方法
不断轮询performance.getEntriesByType()
效率低下。 PerformanceObserver
API提供了一种更好的监听性能条目的方法。 您订阅特定的条目类型,并且浏览器会在记录它们时异步通知您的回调函数。 这是收集性能数据的推荐方法,而不会增加应用程序的开销。
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`Entry Type: ${entry.entryType}, Name: ${entry.name}`);
}
});
observer.observe({ entryTypes: ['resource', 'navigation', 'mark', 'measure'] });
此观察者是不仅收集上述传统指标,而且收集我们接下来将讨论的以用户为中心的现代指标的关键。
转向以用户为中心:Core Web Vitals
知道页面在2秒钟内加载完毕很有用,但是它没有回答关键问题:用户是否在这2秒钟内盯着空白屏幕?他们可以与页面互动,还是页面冻结了?当他们尝试阅读时,内容是否意外地跳来跳去?
为了解决这个问题,Google推出了Core Web Vitals(CWV),这是一组旨在衡量页面在三个关键维度上对真实用户的体验的指标:加载、交互性和视觉稳定性。
最大内容绘制(LCP):测量感知加载
LCP测量视口中可见的最大图像或文本块的渲染时间。当用户感觉页面的主要内容已加载时,这是一个极好的代理。它直接回答了用户的问题:“此页面有用吗?”
- 好: 低于2.5秒
- 需要改进: 在2.5秒和4.0秒之间
- 差: 超过4.0秒
与loadEventEnd
不同,LCP侧重于用户首先看到的内容,使其更能准确反映感知的加载速度。
交互到下一次绘制(INP):测量响应性
INP是首次输入延迟(FID)的继任者,并于2024年3月成为正式的Core Web Vital。虽然FID仅测量*首次*交互的延迟,但INP测量整个页面生命周期中*所有*用户交互(单击、点击、按键)的延迟。它报告最长的交互,有效地识别用户体验到的最坏情况的响应性。
INP测量从用户的输入到绘制下一个帧的整个时间,反映了视觉反馈。它回答了用户的问题:“当我单击此按钮时,页面是否快速响应?”
- 好: 低于200毫秒
- 需要改进: 在200毫秒和500毫秒之间
- 差: 超过500毫秒
高INP通常是由繁忙的主线程引起的,长时间运行的JavaScript任务会阻止浏览器响应用户输入。
累积布局偏移(CLS):测量视觉稳定性
CLS测量页面的视觉稳定性。它量化了加载过程中屏幕上的内容意外移动的程度。高CLS分数是用户沮丧的常见来源,例如,当您尝试单击某个按钮时,广告加载在其上方,将该按钮向下推,导致您单击该广告。
CLS回答了用户的问题:“我可以在不让元素四处跳跃的情况下使用此页面吗?”
- 好: 低于0.1
- 需要改进: 在0.1和0.25之间
- 差: 超过0.25
高CLS的常见原因包括没有尺寸的图像或iframe,Web字体加载延迟或在没有为其保留空间的情况下将内容动态注入到页面中。
弥合差距:使用API诊断糟糕的用户体验
这是所有内容结合在一起的地方。Core Web Vitals告诉我们用户体验到的*内容*(例如,慢速LCP)。Web性能API告诉我们*为什么*会发生这种情况。通过将它们结合起来,我们从简单地观察性能转变为积极地诊断和修复性能。
诊断慢速LCP
假设您的真实用户监控(RUM)工具报告特定区域用户的LCP为4.5秒,表现不佳。 您该如何解决? 您需要将LCP时间分解为组成部分。
- 首字节时间(TTFB): 服务器响应是否缓慢? 使用导航计时API。 持续时间
responseStart - requestStart
为您提供精确的TTFB。 如果此值很高,则问题出在您的后端、服务器配置或数据库上,而不是前端。 - 资源加载延迟和时间: LCP元素本身的加载速度是否缓慢? 首先,确定LCP元素(例如,英雄图像)。 您可以使用
PerformanceObserver
来获取'largest-contentful-paint'
以获取该元素本身。 然后,使用资源计时API查找该元素URL的条目。 分析其时间线:connectStart
到connectEnd
之间是否很长(网络缓慢)?responseStart
到responseEnd
之间是否很长(文件很大)? 其fetchStart
是否因其他渲染阻塞资源(如CSS或JavaScript)而被阻止而延迟? - 元素渲染延迟: 这是资源完成加载后直到实际在屏幕上绘制的时间。 这可能是由于主线程忙于其他任务(例如,执行大型JavaScript捆绑包)引起的。
通过使用导航和资源计时,您可以查明慢速LCP是由于服务器缓慢、渲染阻塞脚本还是大型、未优化的图像引起的。
调查不良INP
您的用户抱怨说单击“添加到购物车”按钮感觉很迟缓。 您的INP指标处于“差”范围内。 这几乎总是主线程问题。
- 识别长时间任务: 长时间任务API是您这里的主要工具。 它报告主线程上花费超过50毫秒的任何任务,因为任何更长的时间都可能使用户注意到延迟。 设置一个
PerformanceObserver
以监听'longtask'
条目。 - 与用户操作相关联: 仅当用户尝试交互时,长时间任务才是一个问题。 您可以将INP事件(通过
PerformanceObserver
在'event'
类型上观察到)的startTime
与在同一时间附近发生的任何长时间任务的计时相关联。 这会告诉您哪个JavaScript函数阻止了用户的交互。 - 测量特定处理程序: 使用用户计时API以获得更精细的粒度。 用
performance.mark()
和performance.measure()
包装您的关键事件处理程序(如“添加到购物车”的“click”处理程序)。 这将告诉您自己的代码执行所需的时间,以及它是否是长时间任务的来源。
处理高CLS
用户报告说,当他们在移动设备上阅读文章时,文本会四处跳跃。 您的CLS得分为0.3。
- 观察布局偏移: 使用
PerformanceObserver
来监听'layout-shift'
条目。 每个条目将具有一个value
(其对CLS得分的贡献)和一个sources
列表,这些是移动的DOM元素。 这会告诉您*什么*移动了。 - 找到罪魁祸首资源: 下一个问题是*为什么*它移动了。 一个常见原因是资源加载延迟并向下推动其他内容。 您可以将
layout-shift
条目的startTime
与资源计时API中条目的responseEnd
时间相关联。 如果在广告脚本或大型图像完成加载后立即发生布局偏移,则您可能已经找到了罪魁祸首。 - 主动解决方案: 修复通常涉及为图像和广告提供尺寸(
<img width="1000" height="600">
),或在页面上为动态内容加载之前保留空间。 资源计时可帮助您识别需要主动处理的资源。
实际实施:构建全球监控系统
了解这些API是一回事;部署它们以监视您的全球用户群的体验是下一步。 这是真实用户监控(RUM)的领域。
使用PerformanceObserver
将所有内容放在一起
您可以创建一个功能强大的脚本来收集所有这些关键数据。 目标是在不影响您尝试测量的性能的情况下收集指标及其上下文。
这是一个强大的观察者设置的概念片段:
const collectedMetrics = {};
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
collectedMetrics.lcp = entry.startTime;
} else if (entry.entryType === 'layout-shift') {
collectedMetrics.cls = (collectedMetrics.cls || 0) + entry.value;
} else if (entry.entryType === 'event') {
// This is a simplified view of INP calculation
const duration = entry.duration;
if (duration > (collectedMetrics.inp || 0)) {
collectedMetrics.inp = duration;
}
}
// ... and so on for other entry types like 'longtask'
}
});
observer.observe({ entryTypes: ['largest-contentful-paint', 'layout-shift', 'event', 'longtask'] });
可靠地发送数据
收集数据后,您需要将其发送到分析后端以进行存储和分析。 这样做至关重要,而不会延迟页面卸载或丢失快速关闭标签页的用户的数据。
navigator.sendBeacon()
API非常适合此目的。 它提供了一种可靠的、异步的方式将少量数据发送到服务器,即使页面正在卸载。 它不期望响应,使其轻量级且非阻塞。
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = JSON.stringify(collectedMetrics);
navigator.sendBeacon('/api/performance-analytics', payload);
}
});
全球视野的重要性
像Lighthouse这样的实验室测试工具非常宝贵,但它们在受控环境中运行。 从这些API收集的RUM数据告诉您用户在不同国家/地区、网络条件和设备上的实际体验。
分析数据时,始终对其进行分段。 您可能会发现:
- 对于北美用户,您的LCP非常好,但对于澳大利亚用户来说,由于您的主要图像服务器位于美国,因此很差。
- 在在新兴市场中流行的中端Android设备上,您的INP很高,因为您的JavaScript对于它们来说CPU消耗太大。
- 只有在CSS媒体查询导致广告调整大小不正确的情况下,CLS才会在特定屏幕尺寸上出现问题。
这种级别的分段洞察力使您可以优先进行优化,这将对您的实际用户群产生最大的影响,无论他们在哪里。
结论:从测量到掌握
Web性能的世界已经成熟。 我们已经从简单的技术计时发展到对用户感知体验的深刻理解。 此过程涉及三个关键步骤:
- 衡量体验: 使用
PerformanceObserver
收集Core Web Vitals(LCP、INP、CLS)。 这会告诉您*正在发生的事情*以及用户*的感觉*。 - 诊断原因: 使用基础计时API(导航、资源、用户、长时间任务)进行深入研究。 这会告诉您*为什么*体验不佳。
- 精准行动: 使用组合数据进行明智的、有针对性的优化,以解决特定用户群问题的根本原因。
通过掌握高级用户指标和低级诊断API,您可以构建一个整体性能策略。 您停止猜测并开始设计一种Web体验,这种体验不仅在技术上速度很快,而且对世界各地每种设备上的每个用户都感觉快速、响应迅速且令人愉快。