Timepot

Time marker & report for page performance testing.
用于 Web 页面性能测速的打点统计及上报。
English Version(@todo) | 中文版
快速上手
1. 安装
npm install timepot --save
2. 使用
引入timepot
后,即可随时打点;在需要时输出或上报。
timepot.mark(); // anonymous mark
/* SOME CODE HERE */
timepot.mark('rendered'); // named mark
// promise api
timepot.timing().then(function(result) {
console.log(result);
// report to server
timepot.report('SOME_URL', timingData => {
// do some formatting for timing data that would be sent
return timingData;
});
});
简化的秒表模式
秒表模式:参数简化版,仅关注本次计时的 group 名称,不关心每个打点的命名,自动采用 tick + index
递增命名。
timepot.start('groupName')
:开始统计,一个 groupName 仅调用一次timepot.tick('groupName')
:记下一个时刻,可多次调用 ticktimepot.stop('groupName')
:结束统计,一个 groupName 仅调用一次
如对单个网络请求测速,在请求开始前timepot.start('cgi')
,在获取到数据后timepot.stop('cgi')
。
特点
- 简单易用,两种模式:打点模式和秒表模式
- 多节点:随时需要,随时
mark
- 多实例:按
group
名称分组统计,互不干扰 - 数据完整:对接 performance 数据,优先基于
PerformanceObserver
获取完整数据 - 支持常见性能评估指标:对标Google RAIL 性能模型
- 优化上报方式:支持
navigator.sendBeacon()
上报,可降级为fetch、XHR - 数据不丢失:支持延时批量上报,剩余未上报内容将会在页面
beforeunload
时发送
设计原理
数据结构
每个打点数据包装为一个Point
,结构为:
Point = {
group: '', // optional, group name
name: '', // optional, name of this point
time: 0, // optional, current time in ms
duration: 0, // optional, time cost, calculate automatically
context: {} // optional, context data
}
每次timepot.mark()
调用时,会写入一次上述结构,并计算duration的值。
内置有如下的group,注意不要覆盖:
- performance: 原始性能指标分组,对应常量
timepot.GROUP_PERFORMANCE
,window.performance.timing
原始值的统计 - audits: 常见性能指标分组,对应常量
timepot.GROUP_AUDITS
,基于window.performance
的进行关键性能指标计算后的统计 - default: 匿名分组,对应常量
timepot.GROUP_DEFAULT
,对于匿名(未命名)打点的统计结果
性能指标
根据 Navigation Timing Processing Model,对 RUM 进行计算的关键渲染路径指标 CRP 如下表。
指标 | 计算方法 | 含义 |
---|---|---|
unload |
.unloadEventEnd - .unloadEventStart | 如果非直接打开时有值 |
redirect |
.redirectEnd - .redirectStart | 同上 |
appCache |
.domainLookupStart - .fetchStart | 读取缓存耗时,如果存在缓存,则直接跳到requestStart阶段 |
DNS |
.domainLookupEnd - .domainLookupStart | DNS查询耗时 |
connect |
.connectEnd - .connectStart | TCP建立连接耗时 |
SSL |
.connectEnd - .secureConnectionStart | 非https请求则无此项 |
TTFB |
.responseStart - .requestStart | 浏览器从发起请求到收到第一个字节的回包响应 |
exchange |
.responseEnd - .requestStart | 网络传输耗时,从发起请求,到收到所有回包内容 |
DOMParse |
.domInteractive - .domLoading | DOM解析时间 |
DOMContentLoaded |
.domContentLoadedEventStart - .domLoading | DOM 和 CSSOM均准备就绪 |
DOMContentLoadedEvent |
.domContentLoadedEventEnd - .domContentLoadedEventStart | DOMContentLoaded事件的执行耗时 |
DOMComplete |
.domComplete - .domLoading | 页面和所有子资源准备就绪 |
loadEvent |
.loadEventEnd - .loadEventStart | onload事件的执行耗时 |
loaded |
.loadEventEnd - .navigationStart | 页面加载完成总耗时 |
FP |
first-paint | 首次绘制 |
FCP |
first-contentful-paint | 首次内容绘制 |
DNS::[domain] |
.domainLookupEnd - .domainLookupStart | 每个域名下最耗时的域名查询,如果cache,则无此项 |
exchange::[domain] |
.responseEnd - .requestStart | 每个域名下最耗时的网络传输,context会列明具体的url、是否压缩、传输大小 |
API
配置参数
通过timepot.config
的属性进行全局设置:
enablePerformance
:是否允许统计performance.timing
数据,默认trueenableSendBeacon
:发送数据时,是否启用navigator.sendBeacon()
方法,默认trueenableFetch
:发送数据时,是否启用fetch()
方法,默认truereportPollingTime
:上报的延时时间窗口,达到此阈值则上报,默认1000ms
方法
方法 | 作用 | 参数解释 |
---|---|---|
timepot.mark(name, point) |
打点 | name : optional,打点名称,无则匿名,会被统计为default分组;point :Optional,打点的其余信息 |
timepot.start(group) |
计时器,开始 | group :required,String |
timepot.tick(group) |
计时器,打下一个计时点 | group :required,String,值同start |
timepot.stop(group) |
计时器,结束 | group :required,String,值同start |
timepot.timing() |
统计耗时,Promise 接口 | - |
timepot.report(url, dataHandler, options) |
上报数据 | url :上报的服务端地址;dataHandler :上报的数据的处理函数,需要return有效上报值;options :选项,见下文 |
timepot.stopReport() |
停止上报 | - |
timepot.getRawTimingData() |
获取原始测速数据 | - |
timepot.getTimingGroup(group) |
按分组获取测速数据 | group :required,String,分组名称 |
timepot.getPerformance() |
获取performance性能测速数据 | - |
timepot.getAudits() |
获取常见性能指标数据 | - |
timepot.formatTimingDataByGroup(data) |
按group格式化原始测速数据 | data :required,Array,待格式化的原始测速数据 |
timepot.clear() |
清除所有测速数据 | - |
timepot.console() |
以表格形式在控制台打印测速数据 | - |
timepot.report()
支持的 options:
- options.reportPollingTime: polling to report
- options.inRealtime: if need sent data in realtime
- options.enableSendBeacon: if enable navigator.sendBeacon()
- options.enableFetch: if enable fetch()
FAQ
1. 统计引入timepot
之前的时间测速
在文档开始处(如)预埋小段代码,加载timepot
后,对应的数据不会丢失;在完成timepot
加载前,仅支持timepot.mark()
方法。
<script type="text/javascript">
window.timepot = window.timepot || [];
timepot.mark = timepot.mark || function(name, point) {
!point && (point = {});
name && (point.name = name);
point.time = Date.now();
timepot.push(point);
};
</script>
2. 在一个页面区分多个统计实例
需要对每个实例进行命名,以group区分。
timepot.mark('start', { group: 'page' });
/* SOME CODE HERE */
timepot.mark('end', { group: 'page' });
如上,则以page
聚拢此组统计,可通过timepot.getTimingGroup('page')
得到测速结果。
外部依赖
小巧,无外部依赖,原生实现。
License
MIT
If you have any questions that aren't covered here, please let me know.