Copyright © 2025 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
本规范扩展了高精度时间规范 [HR-TIME-3],通过提供方法来存储和检索高精度性能指标数据。
本节描述了本文档在发布时的状态。当前 W3C 发布文档及本技术报告的最新修订可在 W3C 标准与草案索引 页面 https://www.w3.org/TR/ 查阅。
本《性能时间线》规范取代了第一版 [PERFORMANCE-TIMELINE],并包括:
Performance
接口的基础定义;
PerformanceEntry
接口
[WORKERS];
PerformanceObserver
的支持。
本文档由 网页性能工作组以 推荐标准流程 发布为候选推荐草案。
作为候选推荐发布,并不代表 W3C 及其成员的认可。候选推荐草案整合了工作组打算在后续候选推荐快照中包含的先前候选推荐版本的更改。
本文档为草稿,有可能随时更新、替换或废弃。仅作为正在进行中的工作引用合适,不应作为正式引用。
本文档由遵循 W3C 专利政策的工作组制作。 W3C 维护着 公开的专利披露列表, 涉及该工作组的成果;该页面还包含了披露专利的说明。任何个人如确实知晓某专利且认为其包含 必要权利要求 ,须根据 《W3C专利政策》第6节 披露相关信息。
本文档受 2023年11月3日 W3C流程文件 管辖。
本节为非规范性内容。
准确衡量 Web 应用的性能特性是提升 Web 应用速度的重要因素。本规范定义了必要的 性能时间线 原语,使 Web 开发者能够访问、监控和获取 Web 应用全生命周期中的各种性能指标。
[NAVIGATION-TIMING-2]、[RESOURCE-TIMING-2] 和 [USER-TIMING-2] 是分别定义与文档导航、页面资源和开发者脚本相关的计时信息的规范实例。这些及其他性能接口共同定义了描述 Web 应用 性能时间线 的性能指标。例如,下面的脚本展示了开发者如何访问 性能时间线,以获取与文档导航、页面资源和开发者脚本相关的性能指标:
<!doctype html>
<html>
<head></head>
<body onload="init()">
<img id="image0" src="https://www.w3.org/Icons/w3c_main.png" />
<script>
function init() {
// 见 [[USER-TIMING-2]]
performance.mark("startWork");
doWork(); // 一些开发者代码
performance.mark("endWork");
measurePerf();
}
function measurePerf() {
performance
.getEntries()
.map(entry => JSON.stringify(entry, null, 2))
.forEach(json => console.log(json));
}
</script>
</body>
</html>
另外,开发者可以通过 性能时间线,通过 PerformanceObserver
接口,接收新产生的性能指标以及(可选)已缓冲的指定类型性能指标的通知。
PerformanceObserver
接口被添加,旨在解决第一个示例中基于缓冲区方式的局限。通过使用 PerformanceObserver 接口,应用可以:
鼓励开发者在可能的情况下使用 PerformanceObserver
。此外,新的性能 API 和指标可能只通过 PerformanceObserver
接口提供。该观察者通过在构造函数中指定回调,并通过 observe()
方法指定所关注的性能条目类型来工作。
用户代理决定何时执行回调,并将已经排队的性能条目传递给回调。
使用 PerformanceObserver
接口时,初始页面加载有特殊考虑:必须有一个活动的注册才能接收事件,但注册脚本可能不可用或不希望处于关键路径。为此,用户代理会在页面构建期间缓冲一定数量的事件,这些缓冲的事件可通过注册观察者时设置 buffered
标志来访问。
设置此标志时,用户代理会检索并分派所缓冲的、指定条目类型的事件,并在 observe()
调用后的第一次回调中传递它们。
缓冲事件的数量由定义该指标的规范决定,缓冲旨在用于前 N 个事件;缓冲不是无限或持续的。
<!doctype html>
<html>
<head></head>
<body>
<img id="image0" src="https://www.w3.org/Icons/w3c_main.png" />
<script>
// 知道我们希望使用的条目类型不被支持时。
function detectSupport(entryTypes) {
for (const entryType of entryTypes) {
if (!PerformanceObserver.supportedEntryTypes.includes(entryType)) {
// 向客户端分析指示 |entryType| 不被支持。
}
}
}
detectSupport(["resource", "mark", "measure"]);
const userTimingObserver = new PerformanceObserver(list => {
list
.getEntries()
// 获取我们关注的值
.map(({ name, entryType, startTime, duration }) => {
const obj = {
"Duration": duration,
"Entry Type": entryType,
"Name": name,
"Start Time": startTime,
};
return JSON.stringify(obj, null, 2);
})
// 输出到控制台。
.forEach(console.log);
// 处理完事件后断开连接。
userTimingObserver.disconnect();
});
// 订阅 User-Timing 的新事件。
userTimingObserver.observe({entryTypes: ["mark", "measure"]});
const resourceObserver = new PerformanceObserver(list => {
list
.getEntries()
// 获取我们关注的值
.map(({ name, startTime, fetchStart, responseStart, responseEnd }) => {
const obj = {
"Name": name,
"Start Time": startTime,
"Fetch Start": fetchStart,
"Response Start": responseStart,
"Response End": responseEnd,
};
return JSON.stringify(obj, null, 2);
})
// 输出到控制台。
.forEach(console.log);
// 处理完事件后断开连接。
resourceObserver.disconnect();
});
// 获取缓冲事件并订阅 Resource Timing 的新事件。
resourceObserver.observe({type: "resource", buffered: true});
</script>
</body>
</html>
除非特别标注为非规范性,所有编写指南、图示、示例和注释均为非规范性内容。本规范中的其他内容均为规范性内容。
本文档中的关键字 MUST、MUST NOT 和 SHOULD 按 BCP 14 [RFC2119] [RFC8174] 的规定进行解释,仅当这些词以大写形式出现时,才按此解释。
以算法或特定步骤表述的一致性要求可用任何方式实现,只要最终结果等效即可。(特别地,本规范定义的算法旨在易于理解,并非强调性能。)
每个 全局对象 包含:
PerformanceEntry
对象,初始为空。
每个 Document 包含:
PerformanceEntry
,初始未设置。
要获取 相关性能条目元组,输入 entryType 和 globalObject,执行以下步骤:
Performance
接口
这是对来自 [HR-TIME-3] 的 Performance
接口的扩展,包含用于从 性能时间线 获取性能指标数据的相关属性和方法。
WebIDLpartial interface Performance {
PerformanceEntryList
getEntries
();
PerformanceEntryList
getEntriesByType
(DOMString type);
PerformanceEntryList
getEntriesByName
(DOMString name, optional DOMString type);
};
typedef sequence<PerformanceEntry
> PerformanceEntryList
;
PerformanceEntryList
表示 PerformanceEntry
的一组序列,提供开发者在 JavaScript
数组上的所有便捷方法。
返回一个 PerformanceEntryList
对象,由 按名称和类型过滤缓冲区映射 算法返回,其中 name
和 type 均为 null
。
返回一个 PerformanceEntryList
对象,由 按名称和类型过滤缓冲区映射算法返回,其中 name
设为 null
,type 设为方法输入的 type
参数。
返回一个 PerformanceEntryList
对象,由 按名称和类型过滤缓冲区映射算法返回,其中 name
设为方法输入的 name
参数,type 若省略可选参数 entryType
则设为
null
,否则设为方法输入的 type
参数。
PerformanceEntry
接口承载各种性能指标的数据。
WebIDL[Exposed=(Window,Worker)]
interface PerformanceEntry
{
readonly attribute unsigned long long id
;
readonly attribute DOMString name
;
readonly attribute DOMString entryType
;
readonly attribute DOMHighResTimeStamp startTime
;
readonly attribute DOMHighResTimeStamp duration
;
[Default] object toJSON
();
};
name
PerformanceEntry
对象的标识符。该标识符不要求唯一。
entryType
所有 entryType
值都在相关注册表中定义。
例如:"mark"
和 "measure"
[USER-TIMING-2],"navigation"
[NAVIGATION-TIMING-2],
以及 "resource"
[RESOURCE-TIMING-2]。
startTime
duration
duration
属性的 getter 步骤为:当 this 的 结束时间
为 0 时返回 0;否则返回 this 的 结束时间 -
this 的 startTime
。
navigationId
调用 toJSON
时,执行 [WebIDL] 的 默认 toJSON 步骤。
PerformanceEntry
有一个 DOMHighResTimeStamp
结束时间,
初始为 0。
初始化 PerformanceEntry
entry,给定
DOMHighResTimeStamp
startTime,
DOMString
entryType,DOMString
name,以及可选 DOMHighResTimeStamp
endTime(默认 0
):
PerformanceObserver
接口可用于观察
性能时间线,以便在记录新性能指标时收到通知,并可选地接收已缓冲的性能指标。
每个 PerformanceObserver
具有以下关联概念:
PerformanceObserverCallback
创建时设置的 观察者回调。
PerformanceEntryList
对象,称为 观察者缓冲区,初始为空。
DOMString
观察者类型,初始为 "undefined"
。
PerformanceObserver(callback)
构造函数必须创建一个新的
PerformanceObserver
对象,其 观察者回调
设置为 callback,然后返回该对象。
已注册性能观察者 是一个 结构体
,包含 观察者成员(一个 PerformanceObserver
对象)和 选项列表成员(一个
PerformanceObserverInit
字典列表)。
WebIDLcallback PerformanceObserverCallback
= undefined (PerformanceObserverEntryList
entries,
PerformanceObserver
observer,
optional PerformanceObserverCallbackOptions
options = {});
[Exposed=(Window,Worker)]
interface PerformanceObserver
{
constructor
(PerformanceObserverCallback
callback);
undefined observe
(optional PerformanceObserverInit
options = {});
undefined disconnect
();
PerformanceEntryList
takeRecords
();
[SameObject] static readonly attribute FrozenArray<DOMString> supportedEntryTypes
;
};
为了将性能开销降至最低,应用应只订阅自己感兴趣的事件类型,并在不再需要观察性能数据时断开观察者。按名称过滤不被支持,因为这会隐式要求订阅所有事件类型——虽然可以实现,但不推荐,因为这会产生大量事件。
WebIDLdictionary PerformanceObserverCallbackOptions
{
unsigned long long droppedEntriesCount
;
};
droppedEntriesCount
PerformanceObserver
的 需要丢弃条目
被设置时,所观察条目类型的丢弃条目计数。observe()
方法指示用户代理注册观察者,必须执行以下步骤:
entryTypes
和 type
成员都未指定,则 抛出 "TypeError
"。
entryTypes
存在且有其他成员同时存在,则
抛出 "TypeError
"。
"undefined"
:
"single"
且 options 的 entryTypes
成员存在,则 抛出
"InvalidModificationError
"。
"multiple"
且 options 的 type
成员
存在,则 抛出
"InvalidModificationError
"。
"multiple"
,执行以下步骤:
entryTypes
序列。
"single"
。
type
不包含在
relevantGlobal 的 受支持条目类型的冻结数组中,则终止这些步骤。用户代理应当通知开发者发生此情况,比如通过控制台警告。
buffered
标志被设置:
一个 PerformanceObserver
对象必须始终使用
options 的 entryTypes
或
options 的 type
调用 observe()
。
如果一个 PerformanceObserver
同时以 entryTypes
和 type
方式调用
observe,则会抛出异常。这是为避免调用堆叠的混乱。当使用 entryTypes
时,PerformanceObserverInit
的其他参数不能使用。此外,
多次 observe()
调用会覆盖旧值以保证兼容性,同时通常只需一次调用。另一方面,
使用 type
时,调用会堆叠,因为单次调用只能指定一个类型。重复调用 observe()
,指定同一个 type
,也会覆盖前者。
WebIDLdictionary PerformanceObserverInit
{
sequence<DOMString> entryTypes
;
DOMString type
;
boolean buffered
;
};
entryTypes
type
buffered
WebIDL[Exposed=(Window,Worker)]
interface PerformanceObserverEntryList
{
PerformanceEntryList
getEntries
();
PerformanceEntryList
getEntriesByType
(DOMString type);
PerformanceEntryList
getEntriesByName
(DOMString name, optional DOMString type);
};
每个 PerformanceObserverEntryList
对象都关联一个
条目列表,
该列表由 PerformanceEntryList
组成,并在构造时初始化。
返回一个 PerformanceEntryList
对象,由 按名称和类型过滤缓冲区 算法返回,参数为 this 的 条目列表,name 和 type 均为
null
。
返回一个 PerformanceEntryList
对象,由 按名称和类型过滤缓冲区 算法返回,参数为 this 的 条目列表,name 为 null
,type
为方法输入的 type
参数。
返回一个 PerformanceEntryList
对象,由 按名称和类型过滤缓冲区 算法返回,参数为 this 的 条目列表,name 为方法输入的 name
参数,type 若省略可选参数 entryType
则为 null
,否则为方法输入的
type
参数。
takeRecords()
方法必须返回 this 的 观察者缓冲区 的副本,并同时清空 this 的 观察者缓冲区。
disconnect()
方法必须执行以下操作:
supportedEntryTypes
属性每个 全局对象都有一个相关的 受支持条目类型的冻结数组,其初始化为按照字母顺序排列、在注册表中针对该全局对象支持的字符串序列,通过 FrozenArray 创建。
当调用 supportedEntryTypes
属性的 getter 时,执行以下步骤:
该属性允许 Web 开发者方便地了解用户代理支持哪些条目类型。
要队列 PerformanceEntry (newEntry),执行以下步骤:
id
未设置:
PerformanceObserver
对象集合。
entryType
值。
navigationId
设为
relevantGlobal 的 关联文档的
最近一次导航的 id
。
navigationId
设为 null。如果 regObs 的 选项列表包含一个 PerformanceObserverInit
options,其 entryTypes
成员包含 entryType 或其 type
成员等于
entryType:
当要求为 PerformanceObserver 排队任务时,输入为 relevantGlobal,执行以下步骤:
PerformanceObserverEntryList
,
其 条目列表 设为 entries。
PerformanceObserverInit
item:
PerformanceObserverCallbackOptions
,
如果 droppedEntriesCount 非 null,则将其 droppedEntriesCount
设为 droppedEntriesCount,否则为未设置。report
",
和 po。
性能时间线 任务队列是一个低优先级队列,用户代理应尽量在空闲时处理,以最大限度降低性能监控代码的影响。
当要求执行 按名称和类型过滤缓冲区映射 算法,输入为可选 name 和 type 时,执行以下步骤:
startTime
对 results
的条目进行时间顺序排序
当要求运行 按名称和类型过滤缓冲区 算法,输入为 buffer、name 和 type,执行以下步骤:
要判断性能条目缓冲区是否已满, 输入为 tuple,执行以下步骤:
当要求为 PerformanceEntry
entry 生成 id 时,执行以下步骤:
用户代理可以选择每次将 最后性能条目 id 增加一个小的随机整数。用户代理不得选择一个全局随机整数并将所有全局对象的 最后性能条目 id 增加该值,因为这可能导致跨域泄漏。
最后性能条目 id 初始为随机值,且每次增加一个由用户代理选择的小数值,而不是 1,以避免开发者将其视为已生成条目数量的计数器。
本规范扩展了 [HR-TIME-3] 定义的 Performance
接口,并提供了方法用于在 性能时间线 中队列和检索条目。有关高精度时间信息暴露的隐私注意事项,请参阅 [HR-TIME-3]。每个新增性能条目的规范都应有自己的隐私注意事项。
最后性能条目 id 故意初始化为随机值,并在每次队列新的 PerformanceEntry
时增加一个小值。用户代理可以选择对所有用户统一递增,也可以为每个
全局对象选择不同的递增,或者每个 PerformanceEntry
随机选择递增值。然而,为防止跨域信息泄露和指纹识别,用户代理不得为所有 PerformanceEntry
对象选择唯一的随机整数并用于所有 全局对象的递增。
本规范扩展了 [HR-TIME-3] 定义的 Performance
接口,并提供了方法用于在 性能时间线 中队列和检索条目。有关高精度时间信息暴露的安全性注意事项,请参阅 [HR-TIME-3]。每个新增性能条目的规范都应有自己的安全性注意事项。
WebIDLpartial interface Performance {
PerformanceEntryList
getEntries
();
PerformanceEntryList
getEntriesByType
(DOMString type);
PerformanceEntryList
getEntriesByName
(DOMString name, optional DOMString type);
};
typedef sequence<PerformanceEntry
> PerformanceEntryList
;
[Exposed=(Window,Worker)]
interface PerformanceEntry
{
readonly attribute unsigned long long id
;
readonly attribute DOMString name
;
readonly attribute DOMString entryType
;
readonly attribute DOMHighResTimeStamp startTime
;
readonly attribute DOMHighResTimeStamp duration
;
readonly attribute unsigned long long navigationId
;
[Default] object toJSON
();
};
callback PerformanceObserverCallback
= undefined (PerformanceObserverEntryList
entries,
PerformanceObserver
observer,
optional PerformanceObserverCallbackOptions
options = {});
[Exposed=(Window,Worker)]
interface PerformanceObserver
{
constructor
(PerformanceObserverCallback
callback);
undefined observe
(optional PerformanceObserverInit
options = {});
undefined disconnect
();
PerformanceEntryList
takeRecords
();
[SameObject] static readonly attribute FrozenArray<DOMString> supportedEntryTypes
;
};
dictionary PerformanceObserverCallbackOptions
{
unsigned long long droppedEntriesCount
;
};
dictionary PerformanceObserverInit
{
sequence<DOMString> entryTypes
;
DOMString type
;
boolean buffered
;
};
[Exposed=(Window,Worker)]
interface PerformanceObserverEntryList
{
PerformanceEntryList
getEntries
();
PerformanceEntryList
getEntriesByType
(DOMString type);
PerformanceEntryList
getEntriesByName
(DOMString name, optional DOMString type);
};
感谢 Arvind Jain、Boris Zbarsky、Jatinder Mann、Nat Duca、Philippe Le Hegaret、Ryosuke Niwa、Shubhie Panicker、Todd Reifsteck、Yoav Weiss 以及 Zhiheng Wang 对本工作做出的贡献。
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: