您可以在任何给定网站上普遍衡量以用户为中心的指标,享受诸多价值。通过这些指标,您可以:
- 了解真实用户的整体网络体验。
- 将您的网站与竞争对手的网站进行比较。
- 无需编写自定义代码,即可在分析工具中跟踪实用且富有实用价值的数据。
通用指标提供了一个很好的基准,但在许多情况下,除了这些指标之外,您还需要衡量更多指标,以便获得特定网站的完整体验。
通过自定义指标,您可以衡量网站体验中可能仅适用于您网站的各个方面,例如:
- 单页应用 (SPA) 从一个“页面”转换需要多长时间到另一个位置。
- 网页针对已登录用户显示从数据库中提取的数据所需的时间。
- 服务器端渲染 (SSR) 应用水合所需的时间。
- 回访者所加载资源的缓存命中率。
- 游戏中点击或键盘事件的事件延迟时间。
用于衡量自定义指标的 API
过去,网络开发者没有太多的低级 API 来衡量性能,因此他们不得不依靠黑客手段来衡量网站是否表现良好。
例如,通过运行 requestAnimationFrame
循环并计算每个帧之间的增量,可以确定主线程是否因长时间运行的 JavaScript 任务而处于阻塞状态。如果增量明显长于显示屏的帧速率,则可将其报告为耗时较长的任务。但我们不建议采用此类黑客手段,因为它们实际上会影响性能(例如,通过消耗电池电量)。
有效衡量效果的首要原则是确保您的效果衡量技术本身不会导致性能问题。因此,对于您在网站上衡量的任何自定义指标,最好尽可能使用以下某个 API。
Performance Observer API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Performance Observer API 是一种机制,用于收集和显示来自本页讨论的所有其他 Performance API 的数据。了解这一点对于获得优质数据至关重要。
您可以使用 PerformanceObserver
被动订阅与性能相关的事件。这使得 API 回调在空闲时段时触发,这意味着它们通常不会干扰页面性能。
如需创建 PerformanceObserver
,请向其传递一个回调,该回调将在分派新的性能条目时运行。然后,您需要使用 observe()
方法告知观察器要监听哪些类型的条目:
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
po.observe({type: 'some-entry-type'});
以下部分列出了可观察的所有条目类型,但在较新的浏览器中,您可以通过静态 PerformanceObserver.supportedEntryTypes
属性检查哪些条目类型可用。
观察已经发生的条目
默认情况下,PerformanceObserver
对象只能在出现条目时观察它们。如果您想延迟加载性能分析代码,以免阻塞优先级较高的资源,就可能会导致问题。
如需获取历史条目(在它们出现之后),请在调用 observe()
时将 buffered
标志设置为 true
。首次调用 PerformanceObserver
回调时,浏览器将包含其性能条目缓冲区中的历史条目(最高可达到该类型的缓冲区大小上限)。
po.observe({
type: 'some-entry-type',
buffered: true,
});
要避免的旧版性能 API
在 Performance Observer API 出现之前,开发者可以使用在 performance
对象中定义的以下三种方法访问性能条目:
虽然这些 API 仍受支持,但不建议使用它们,因为它们不允许您监听新条目的发出时间。此外,许多新 API(例如 largest-contentful-paint
)不是通过 performance
对象公开的,而是仅通过 PerformanceObserver
公开的。
除非您特别需要与 Internet Explorer 兼容,否则最好不要在代码中使用这些方法,并在以后使用 PerformanceObserver
。
User Timing API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
User Timing API 是一种通用的 Measurement API。它支持在任意区域 然后再测量这些标记之间的用时。
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');
// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
虽然 Date.now()
或 performance.now()
等 API 可以提供类似功能,但使用 User Timing API 的好处是它能与性能工具完美集成。例如,Chrome 开发者工具在“性能”面板中直观呈现用户计时数据,许多分析服务提供商还会自动跟踪您进行的任何测量并将时长数据发送至其分析后端。
如需报告 User Timing 测量结果,您可以使用 PerformanceObserver 并进行注册,从而观察 measure
类型的条目:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});
Long Tasks API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Long Tasks API 可用于了解浏览器的主线程何时被阻塞的时间足以影响帧速率或输入延迟。该 API 将报告执行时间超过 50 毫秒的所有任务。
<ph type="x-smartling-placeholder">每当您需要运行昂贵的代码,或者加载和执行大型脚本时,跟踪这些代码是否会阻塞主线程都非常有用。事实上,许多更高级别的指标都是基于 Long Tasks API 本身构建的,例如可交互时间 (TTI) 和总阻塞时间 (TBT)。
如需确定耗时较长的任务的发生时间,您可以使用 PerformanceObserver 并进行注册,以观察 longtask
类型的条目:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});
长动画帧 API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Long Animation Frames API 是 Long Tasks API 的新版本,它处理的是超过 50 毫秒的长帧,而不是长任务。这解决了 Long Tasks API 的一些缺点,包括更好的归因和更广泛的潜在延迟问题。
如需确定何时发生长帧,您可以使用 PerformanceObserver 并注册以观察 long-animation-frame
类型的条目:
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
Element Timing API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Largest Contentful Paint (LCP) 指标有助于了解最大的图片或文本块何时绘制到屏幕上,但在某些情况下,您可能希望测量其他元素的呈现时间。
对于这些情况,请使用 Element Timing API。LCP API 实际上是基于 Element Timing API 构建的,并且添加了针对最大内容元素的自动报告,但您也可以通过向其他元素明确添加 elementtiming
属性并注册 PerformanceObserver 来观察 element
条目类型来报告其他元素。
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
const po = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `element` entries to be dispatched.
po.observe({type: 'element', buffered: true});
</script>
Event Timing API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Interaction to Next Paint (INP) 指标通过观察网页在整个生命周期内的所有点击、点按和键盘互动情况来评估总体网页响应能力。网页的 INP 通常是指从用户发起互动到浏览器绘制下一帧以显示用户输入的视觉结果,完成时间最长的互动。
您可以通过 Event Timing API 使用 INP 指标。此 API 公开了事件生命周期中发生的许多时间戳,包括:
startTime
:浏览器收到事件的时间。processingStart
:浏览器能够开始处理事件的事件处理脚本的时间。processingEnd
:浏览器完成从此事件的事件处理脚本发起的所有同步代码的操作的时间。duration
:浏览器收到事件到能够绘制下一帧所用的时间(出于安全考虑,四舍五入为 8 毫秒)。执行完从事件处理脚本发起的所有同步代码后,浏览器可绘制下一帧。
以下示例展示了如何使用这些值创建自定义测量结果:
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
Resource Timing API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
借助 Resource Timing API,开发者可以详细了解特定网页的资源是如何加载的。尽管 API 的名称是这样,但它提供的信息不仅限于时间数据(尽管有很多)。您可以访问的其他数据包括:
initiatorType
:资源的获取方式:例如从<script>
或<link>
标记,或者fetch()
调用。nextHopProtocol
:用于提取资源的协议,例如h2
或quic
。encodedBodySize
/decodedBodySize]:以编码或解码形式表示的资源大小(分别)transferSize
:通过网络实际传输的资源的大小。当缓存用尽资源时,此值可以比encodedBodySize
小得多,在某些情况下可能为零(如果不需要重新验证缓存)。
您可以使用资源计时条目的 transferSize
属性来测量缓存命中率指标或总缓存资源大小指标,这有助于了解资源缓存策略对重复访问者的性能有何影响。
以下示例记录了网页请求的所有资源,并指明了缓存是否满足了每个资源的要求。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
Navigation Timing API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Navigation Timing API 与 Resource Timing API 类似,但只会报告导航请求。navigation
条目类型也与 resource
条目类型类似,但它包含一些仅适用于导航请求的其他信息(例如,何时触发 DOMContentLoaded
和 load
事件)。
许多开发者跟踪的一个指标是了解服务器响应时间(第一字节时间 (TTFB)),可通过 Navigation Timing API 获取,具体而言是条目的 responseStart
时间戳。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
使用 Service Worker 的开发者可能关心的另一个指标是导航请求的 Service Worker 启动时间。这是浏览器在开始拦截提取事件之前启动 Service Worker 线程所用的时间。
特定导航请求的 Service Worker 启动时间可以根据 entry.responseStart
和 entry.workerStart
之间的增量确定。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Service Worker startup time:',
entry.responseStart - entry.workerStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Server Timing API
浏览器支持
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
利用 Server Timing API,您可以通过响应标头将特定于请求的时间数据从服务器传递到浏览器。例如,您可以指明在数据库中查找特定请求的数据所花费的时间,这对于调试由服务器速度缓慢导致的性能问题非常有用。
对于使用第三方分析服务提供商的开发者而言,Server Timing API 是将服务器性能数据与这些分析工具可能衡量的其他业务指标关联起来的唯一途径。
如需在响应中指定服务器计时数据,您可以使用 Server-Timing
响应标头。示例如下:
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
然后,您可以在页面中读取 Resource Timing API 和 Navigation Timing API 中的 resource
或 navigation
条目的此类数据。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Logs all server timing data for this response
console.log('Server Timing', entry.serverTiming);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});