用户计时

W3C 候选推荐草案

关于本文档的更多细节
此版本:
https://www.w3.org/TR/2026/CRD-user-timing-20260311/
最新发布版本:
https://www.w3.org/TR/user-timing/
最新编辑草案:
https://w3c.github.io/user-timing/
历史:
https://www.w3.org/standards/history/user-timing
提交历史
测试套件:
https://wpt.fyi/results/user-timing
实现报告:
https://wpt.fyi/results/user-timing
编辑:
Nicolás Peña Moreno (Google)
前编辑:
Ilya Grigorik (Google)
Jatinder Mann (Microsoft Corp.) (至 2014 年 2 月)
Zhiheng Wang (Google Inc.) (至 2013 年 7 月)
Anderson Quach (Microsoft Corp.) (至 2011 年 3 月)
反馈:
GitHub w3c/user-timing拉取请求新议题开放议题
public-web-perf@w3.org, 主题行为 [UserTiming]归档
实现
我能使用 User Timing 吗?

摘要

本规范定义了一个接口,通过允许 Web 开发者访问高精度时间戳,帮助他们测量其应用程序的性能。

本文档状态

本节描述本文档在其发布时的状态。当前 W3C 出版物的列表以及本技术报告的最新修订版可在 W3C 标准和草案 索引中找到。

本 User Timing 规范旨在取代 [USER-TIMING-2],并包括:

本文档由 Web Performance Working Group 作为候选推荐草案发布,使用的是 推荐标准轨道

作为候选推荐发布并不 意味着 W3C 及其成员的认可。候选 推荐草案整合了工作组 打算纳入后续候选推荐快照的、相对于前一候选推荐所作的变更。

这是一份草案文档,随时可能被更新、替换或废弃并由其他 文档取代。不应将本文档作为进行中的工作以外的内容来引用。

本文档由一个按照 W3C 专利 政策运作的小组制作。 W3C 维护一份 任何专利 披露的公开列表, 这些披露与该小组的交付成果相关;该页面还包括 披露专利的说明。实际 知悉某项专利,且认为该专利包含 必要权利要求的个人, 必须按照 W3C 专利政策第 6 节披露相关信息。

本文档受 2025 年 8 月 18 日 W3C 流程文档管辖。

1. 简介

本节是非规范性的。

Web 开发者需要能够评估和理解其 应用程序的性能特征。虽然 JavaScript [ECMA-262] 提供了一种测量 应用程序延迟的机制(从 Date.now() 方法取得当前时间戳),但该 时间戳的精度会因用户代理而异。

本文档定义了 PerformanceMarkPerformanceMeasure 接口,以及对 Performance 接口的扩展,它们公开高 精度、单调递增的时间戳,使开发者能够更好地测量其应用程序的性能 特征。

下面的脚本展示了开发者如何使用本文档定义的接口来获得 与开发者脚本相关的计时数据。

async function run() {
  performance.mark("startTask1");
  await doTask1(); // 一些开发者代码
  performance.mark("endTask1");

  performance.mark("startTask2");
  await doTask2(); // 一些开发者代码
  performance.mark("endTask2");

  // 将它们记录出来
  const entries = performance.getEntriesByType("mark");
  for (const entry of entries) {
    console.table(entry.toJSON());
  }
}
run();

[PERFORMANCE-TIMELINE-2] 定义了两种 可用于检索已记录指标的机制:getEntries()getEntriesByType() 方法,以及 PerformanceObserver 接口。前者最适合 你想要在某个单一时间点按名称检索特定指标的场景, 后者则针对你可能希望在新指标变得 可用时接收通知的场景进行了优化。

再举一个例子,假设有一个元素,当它被 点击时,会获取一些新内容并指示该内容已获取完成。 我们想要报告从用户点击到获取 完成之间的时间。我们不能标记点击处理程序执行的时间,因为那样 会漏掉处理事件的延迟,所以我们改用事件的 硬件时间戳。我们还想知道组件名称,以便获得 更详细的分析数据。

element.addEventListener("click", e => {
  const component = getComponent(element);
  fetch(component.url).then(() => {
    element.textContent = "Updated";
    const updateMark = performance.mark("update_component", {
      detail: {component: component.name},
    });
    performance.measure("click_to_update_component", {
      detail: {component: component.name},
      start: e.timeStamp,
      end: updateMark.startTime,
    });
  });
});

2. 一致性

除标记为非规范性的章节外,本规范中的所有编写指南、图示、示例和注释都是 非规范性的。本规范中的其他所有内容都是规范性的。

本文档中的关键词 MAYMUST 在且仅在它们如这里所示以全大写形式出现时, 应按 BCP 14 [RFC2119] [RFC8174] 中所描述的方式解释。

某些一致性要求以对属性、 方法或对象的要求来表述。这些要求应解释为 对用户代理的要求。

本规范中的 IDL 片段 MUST 按 Web IDL 规范中关于一致性 IDL 片段的描述来解释。 [WEBIDL]

3. User Timing

3.1 Performance 接口的扩展

Performance 接口和 DOMHighResTimeStamp 定义于 [HR-TIME-2]。 PerformanceEntry 接口定义于 [PERFORMANCE-TIMELINE-2]。

WebIDLdictionary PerformanceMarkOptions {
    any detail;
    DOMHighResTimeStamp startTime;
};

dictionary PerformanceMeasureOptions {
    any detail;
    (DOMString or DOMHighResTimeStamp) start;
    DOMHighResTimeStamp duration;
    (DOMString or DOMHighResTimeStamp) end;
};

partial interface Performance {
    PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions = {});
    undefined clearMarks(optional DOMString markName);
    PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions = {}, optional DOMString endMark);
    undefined clearMeasures(optional DOMString measureName);
};

3.1.1 mark() 方法

存储带有关联名称的时间戳(一个“mark”)。它 MUST 运行这些 步骤:

  1. 运行 PerformanceMark 构造器,并令 entry 为新创建的对象。
  2. 排队 entry
  3. entry 添加到 performance entry buffer
  4. 返回 entry
3.1.1.1 PerformanceMarkOptions 字典
detail
要包含在标记中的元数据。
startTime
要用作标记时间的时间戳。

3.1.2 clearMarks() 方法

移除带有关联名称的已存储时间戳。它 MUST 运行这些 步骤:

  1. 如果省略了 markName,则从 PerformanceMark 对象中移除所有位于 performance entry buffer 中的对象。
  2. 否则,移除 PerformanceMark 对象中列在 performance entry buffer 中且其 name markName 的所有对象。
  3. 返回 undefined

3.1.3 measure() 方法

存储两个标记之间的 DOMHighResTimeStamp 持续时间,并附带关联名称(一个“measure”)。它 MUST 运行这些步骤:

  1. 如果 startOrMeasureOptions 是一个 PerformanceMeasureOptions 对象,并且 startenddurationdetail 中至少有一个 存在,则运行以下 检查:
    1. 如果给定了 endMark,则抛出一个 TypeError
    2. 如果 startOrMeasureOptionsstartend 成员 都被省略,则抛出一个 TypeError
    3. 如果 startOrMeasureOptionsstartdurationend 成员全部 存在,则抛出一个 TypeError
  2. 按如下方式计算 end time
    1. 如果给定了 endMark,则令 end time 为传入 endMark 运行 convert a mark to a timestamp 算法所返回的值。
    2. 否则,如果 startOrMeasureOptions 是一个 PerformanceMeasureOptions 对象,并且如果其 end 成员 存在, 则令 end time 为传入 startOrMeasureOptionsend 运行 convert a mark to a timestamp 算法所返回的值。
    3. 否则,如果 startOrMeasureOptions 是一个 PerformanceMeasureOptions 对象,并且如果其 startduration 成员都 存在
      1. start 为传入 start 运行 convert a mark to a timestamp 算法所返回的值。
      2. duration 为传入 duration 运行 convert a mark to a timestamp 算法所返回的值。
      3. end timestart 加上 duration
    4. 否则,令 end timePerformance 对象的 now() 方法将会返回的值。
  3. 按如下方式计算 start time
    1. 如果 startOrMeasureOptions 是一个 PerformanceMeasureOptions 对象,并且如果其 start 成员 存在, 则令 start time 为传入 startOrMeasureOptionsstart 运行 convert a mark to a timestamp 算法所返回的值。
    2. 否则,如果 startOrMeasureOptions 是一个 PerformanceMeasureOptions 对象,并且如果其 durationend 成员都 存在
      1. duration 为传入 duration 运行 convert a mark to a timestamp 算法所返回的值。
      2. end 为传入 end 运行 convert a mark to a timestamp 算法所返回的值。
      3. start timeend 减去 duration
    3. 否则,如果 startOrMeasureOptions 是一个 DOMString,则令 start time 为传入 startOrMeasureOptions 运行 convert a mark to a timestamp 算法所返回的值。
    4. 否则,令 start time0
  4. 使用 this相关 realm 创建一个新的 PerformanceMeasure 对象 (entry)。
  5. entryname 属性设置为 measureName
  6. entryentryType 属性设置为 DOMString "measure"
  7. entrystartTime 属性设置为 start time
  8. entryduration 属性设置为从 start timeend time 的持续时间。所得持续时间值 MAY 为负数。
  9. 按如下方式设置 entrydetail 属性:
    1. 如果 startOrMeasureOptions 是一个 PerformanceMeasureOptions 对象,并且 startOrMeasureOptionsdetail 成员 存在
      1. record 为对 startOrMeasureOptionsdetail 调用 StructuredSerialize 算法的结果。
      2. entrydetail 设置为 以 record当前 realm 调用 StructuredDeserialize 算法的结果。
    2. 否则,将其设置为 null
  10. 排队 entry
  11. entry 添加到 performance entry buffer
  12. 返回 entry
3.1.3.1 PerformanceMeasureOptions 字典
detail
要包含在度量中的元数据。
start
要用作开始时间的时间戳,或要用作开始标记的字符串。
duration
开始时间与结束时间之间的持续时间。
end
要用作结束时间的时间戳,或要用作结束标记的字符串。

3.1.4 clearMeasures() 方法

移除带有关联名称的已存储时间戳。它 MUST 运行这些步骤:

  1. 如果省略了 measureName,则移除 PerformanceMeasure 对象中位于 performance entry buffer 中的所有对象。
  2. 否则,移除 PerformanceMeasure 对象中列在 performance entry buffer 中且其 name measureName 的所有对象。
  3. 返回 undefined

3.2 PerformanceMark 接口

PerformanceMark 接口还会将通过 Performance 接口的 mark() 方法创建的标记暴露给 Performance Timeline

WebIDL[Exposed=(Window,Worker)]
interface PerformanceMark : PerformanceEntry {
  constructor(DOMString markName, optional PerformanceMarkOptions markOptions = {});
  readonly attribute any detail;
};

PerformanceMark 接口扩展了 PerformanceEntry 接口的以下属性:

name 属性必须返回该标记的名称。

entryType 属性必须返回 DOMString "mark"

startTime 属性必须返回一个 DOMHighResTimeStamp, 其值为该标记的时间值。

duration 属性必须返回值为 0DOMHighResTimeStamp

PerformanceMark 接口包含以下 附加属性:

detail 属性必须返回其被设置为的值(它是从 PerformanceMarkOptions 字典复制而来的)。

3.2.1 PerformanceMark 构造器

PerformanceMark 构造器必须运行 以下步骤:

  1. 如果 当前 全局对象是一个 Window 对象,并且 markName 使用了与 PerformanceTiming 接口中的某个只读属性相同的名称, 则抛出一个 SyntaxError
  2. 使用当前 全局对象realm 创建一个新的 PerformanceMark 对象 (entry)。
  3. entryname 属性设置为 markName
  4. entryentryType 属性设置为 DOMString "mark"
  5. 按如下方式设置 entrystartTime 属性:
    1. 如果 markOptionsstartTime 成员 存在,则:
      1. 如果 markOptionsstartTime 为负数,则抛出一个 TypeError
      2. 否则,将 entrystartTime 设置为 markOptionsstartTime 的值。
    2. 否则,将其设置为 Performance 对象的 now() 方法将会返回的值。
  6. entryduration 属性设置为 0
  7. 如果 markOptionsdetail 为 null,则将 entrydetail 设置为 null。
  8. 否则:
    1. record 为对 markOptionsdetail 调用 StructuredSerialize 算法的结果。
    2. entrydetail 设置为 以 record当前 realm 调用 StructuredDeserialize 算法的结果。

3.3 PerformanceMeasure 接口

PerformanceMeasure 接口还会将 通过 Performance 接口的 measure() 方法创建的度量暴露给 Performance Timeline

WebIDL[Exposed=(Window,Worker)]
interface PerformanceMeasure : PerformanceEntry {
  readonly attribute any detail;
};

PerformanceMeasure 接口扩展了 PerformanceEntry 接口的以下属性:

name 属性必须返回该度量的名称。

entryType 属性必须返回 DOMString "measure"

startTime 属性必须返回一个 DOMHighResTimeStamp, 其值为该度量的开始标记。

duration 属性必须返回一个 DOMHighResTimeStamp, 其值为该度量的持续时间。

PerformanceMeasure 接口包含 以下附加属性:

detail 属性必须返回其被设置为的值(它是 从 PerformanceMeasureOptions 字典复制而来的)。

4. 处理

实现 User Timing API 的用户代理需要在 supportedEntryTypes 中包含 "mark""measure"。这允许开发者检测对 User Timing 的支持。

4.1 mark 转换为 timestamp

将标记转换为时间戳,给定一个作为 DOMStringDOMHighResTimeStampmark,运行这些步骤:

  1. 如果 mark 是一个 DOMString,并且它与 PerformanceTiming 接口中的某个只读属性同名,则令 end time 为在 name 设置为 mark 的值时运行 convert a name to a timestamp 算法 所返回的值。
  2. 否则,如果 mark 是一个 DOMString,则令 end timeperformance entry buffer 中其 name markPerformanceMark 对象的最近一次出现中的 startTime 属性值。如果未找到匹配项,则抛出一个 SyntaxError
  3. 否则,如果 mark 是一个 DOMHighResTimeStamp
    1. 如果 mark 为负数,则抛出一个 TypeError
    2. 否则,令 end timemark

4.2 name 转换为 timestamp

将名称转换为 时间戳, 给定一个作为 PerformanceTiming 接口中的只读 属性name,运行这些步骤:

  1. 如果全局 对象不是 Window 对象,则抛出一个 TypeError
  2. 如果 namenavigationStart,则返回 0
  3. startTimePerformanceTiming 接口中 navigationStart 的值。
  4. endTimePerformanceTiming 接口中 name 的值。
  5. 如果 endTime0,则抛出一个 InvalidAccessError
  6. 返回从 endTime 中减去 startTime 的结果。

PerformanceTiming 接口定义于 [NAVIGATION-TIMING],并且 现在被视为已废弃。支持使用来自 PerformanceTiming 接口的名称,是为了保持向后兼容,但未来没有计划将此 功能扩展到 [NAVIGATION-TIMING-2] 中定义的 PerformanceNavigationTiming 接口(或其他接口)中的名称。

6. 隐私与安全

本节是非规范性的。

本规范定义的接口会暴露页面中特定 JavaScript 活动的、可能 敏感的计时信息。有关暴露 高分辨率计时信息的隐私和安全注意事项,请参见 [HR-TIME-2]。

由于 Web 平台的设计遵循这样一个不变原则:页面中包含的任何 脚本都与同一页面中包含的任何其他脚本具有相同的访问权限, 无论这些脚本的源如何, 本规范定义的接口不会对 已记录计时信息的记录或检索施加任何限制——也就是说,页面中包含的任何脚本记录的 user timing mark 或 measure,都可以被 同一页面上运行的任何其他脚本读取,而不论其源如何。

A. 致谢

感谢 James Simonsen、 Jason Weber、 Nic Jansma、 Philippe Le Hegaret、 Karen Anderson、 Steve Souders、 Sigbjorn Vik、 Todd Reifsteck 和 Tony Gentilcore 对此工作的贡献。

B. 参考文献

B.1 规范性参考文献

[HR-TIME-2]
High Resolution Time Level 2. Ilya Grigorik. W3C. 21 November 2019. W3C Recommendation. URL: https://www.w3.org/TR/hr-time-2/
[HTML]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[NAVIGATION-TIMING]
Navigation Timing. Zhiheng Wang. W3C. 17 December 2012. W3C Recommendation. URL: https://www.w3.org/TR/navigation-timing/
[PERFORMANCE-TIMELINE-2]
Performance Timeline. Nicolas Pena Moreno. W3C. 21 May 2025. CRD. URL: https://www.w3.org/TR/performance-timeline/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

B.2 资料性参考文献

[ECMA-262]
ECMAScript Language Specification. Ecma International. URL: https://tc39.es/ecma262/multipage/
[NAVIGATION-TIMING-2]
Navigation Timing Level 2. Yoav Weiss; Noam Rosenthal. W3C. 25 February 2026. W3C Working Draft. URL: https://www.w3.org/TR/navigation-timing-2/
[USER-TIMING-2]
User Timing Level 2. Ilya Grigorik. W3C. 26 February 2019. W3C Recommendation. URL: https://www.w3.org/TR/user-timing-2/