指标计算
UV计算
统计方式,pv统计的基础上,添加唯一标识,可以是IP地址,用户浏览器localStorage存储的uuid等。IP地址存在局域网内的共享一个ip、代理、动态ip等误差,uuid存在用户删除localStorage等,需权衡选取,uuid的准确度更高。
计算Navigation Timing指标
Navigation Timing提供了可用于衡量一个网站性能的数据。与用于相同目的的其他基于JavaScript的机制不同,它可以提供可以更有用和更准确的端到端延迟数据。您可以衡量之前难以获取的数据,如卸载前一个页面的时间,在域名解析上的时间,在执行load事件处理器上花费的总时间等。
Navigation Timing
字段解释
初始化阶段
- navigationStart:用户完成卸载前一个文档的时间点。
- redirectStart:页面重定向时的开始时间(如果存在重定向的话)或者是0。
- redirectEnd:如果存在重定向的话,redirectEnd表示最后一次重定向后服务器端response的数据被接收完毕的时间。否则的话就是0。
请求阶段
- fetchStart:浏览器发起资源请求时,如果有缓存,则返回读取缓存的开始时间。
- domainLookupStart:查询DNS的开始时间。如果请求没有发起DNS请求,如keep-alive,缓存等,则返回fetchStart的时间点。
- domainLookupEnd:查询DNS的结束时间。如果没有发起DNS请求,如keep-alive,缓存等,则返回fetchStart的时间点。
- connectStart:当浏览器开始与服务器连接时的时间。如果没有建立连接,如请求是keep-alive、缓存等,那么它的值等同于domainLookupEnd。
- secureConnectionStart:如果页面使用HTTPS,它的值是安全连接握手之前的时刻。如果该属性不可用,则返回undefined。如果该属性可用,但没有使用HTTPS,则返回0。
- connectEnd:当浏览器端完成与服务器端建立连接的时刻。如果没有建立连接,如请求是keep-alive、缓存等,那么它的值等同于domainLookupEnd。
- responseStart:指客户端收到从服务器端(或缓存、本地资源)响应回的第一个字节的数据的时刻。
- responseEnd:指客户端收到从服务器端(或缓存、本地资源)响应回的最后一个字节的数据的时刻。
解析渲染阶段
- domLoading:浏览器即将开始解析第一批收到的 HTML 文档字节。
- domInteractive:浏览器完成对所有 HTML 的解析并且 DOM 构建完成的时间点, 是DOM 准备就绪的时间点。
- domContentLoaded:DOM 准备就绪并且没有样式表阻止 JavaScript 执行的时间点,可以开始构建渲染树,一般表示 DOM 和 CSSOM 均准备就绪的时间点。
- domComplete:顾名思义,所有处理完成,并且网页上的所有资源(图像等)都已下载完毕,也就是说,加载转环已停止旋转, 表示网页及其所有子资源都准备就绪的时间点。
- loadEventStart:作为每个网页加载的最后一步,浏览器会触发 onload 事件,以便触发额外的应用逻辑。
- loadEventEnd:onload 事件 执行完成。许多 JavaScript 框架都会等待onload事件发生后,才开始执行它们自己的逻辑。因此,浏览器会捕获 loadEventStart 和 loadEventEnd 时间戳来追踪执行所花费的时间。
各阶段计算公式
经过上面各个字段的解释,则可以清晰得出以下公式。
字段 | 公式 | 计算条件 |
---|---|---|
Redirect | redirectEnd - redirectStart | redirectEnd 非0且redirectStart非0 |
DNS | domainLookupEnd - domainLookupStart | domainLookupEnd 非0且domainLookupStart非0 |
TCP | connectEnd - connectStart | connectEnd 非0且connectStart非0 |
SSL | connectEnd - secureConnectionStart | connectEnd 非0且secureConnectionStart非0 |
Request | responseStart - requestStart | responseStart 非0且requestStart非0 |
Response | responseEnd - responseStart | responseEnd 非0且responseStart非0 |
Load | loadEventEnd - navigationStart | loadEventEnd 非0且navigationStart 非0 页面完全加载总时间,是指从NavigationStart事件开始到LoadEventEnd事件结束的时间 |
DomReady | domContentLoaded - fetchStart | domContentLoaded非0且fetchStart非0 |
DomParse | domInteractive-responseEnd | domInteractive非0且responseEnd非0 |
Processing | domComplete - domLoading | domComplete非0且domLoading非0 |
resourceLoad | loadEventStart-domContentLoaded | loadEventStart非0 且 domContentLoaded 非0 |
TTFB | responseStart - navigationStart | - |
Time To First Byte | 发出页面请求到接收到应答数据第一个字节的时间总和,它包含了DNS解析时间、 TCP连接时间、发送HTTP请求时间和获得响应消息第一个字节的时间 | responseStart 非0 且 navigationStart 非0 |
说明
起点选择navigationStart 是为了和web-vitals规范保持一致
计算 FP 指标
- 首次渲染的时间点。 在性能统计指标中,从用户开始访问 Web 页面的时间点到 FP 的时间点这段时间可以被视为 白屏时间,也就是说在用户访问 Web 网页的过程中,FP 时间点之前,用户看到的都是没有任何内容的白色屏幕,用户在这个阶段感知不到任何有效的工作在进行。
- 所以通常会反映页面的白屏时间,而白屏时间会反映当前 Web 页面的网络加载性能情况,当加载性能非常良好的情况下,白屏的时间就会越短,用户等待内容的时间就会越短,流失的概率就会降低。
通过 W3C Paint Timing 规范草案 中的描述, PerformancePaintTiming 中包含当前 Web 页面的绘制性能打点信息,可通过 performance.getEntriesByType('paint') 方法获取。
找到 name 为 first-paint 的对象,描述的即为 FP 的指标数据,其中 startTime 即为 FP 时间。
计算 FCP指标
- 首次有内容渲染的时间点。 在性能统计指标中,从用户开始访问 Web 页面的时间点到 FCP 的时间点这段时间可以被视为 无内容时间,也就是说在用户访问 Web 网页的过程中,FCP 时间点之前,用户看到的都是没有任何实际内容的屏幕,用户在这个阶段获取不到任何有用的信息。
- 所以通常会反映页面的首次出现内容的时间,而首次出现内容时间会反映当前 Web 页面的网络加载性能情况、页面 DOM 结构复杂度情况、inline script 的执行效率的情况,当所有的阶段性能做的非常好的情况下,首次出现内容的时间就会越短,用户等待的时间就会越短,流失的概率就会降低。
- 首次内容绘制 (FCP) 指标测量页面从开始加载到页面内容的任何部分在屏幕上完成渲染的时间。对于该指标,"内容"指的是文本、图像(包括背景图像)、
<svg>
元素或非白色的<canvas>
元素。
通过 W3C Paint Timing 规范草案 中的描述, PerformancePaintTiming 中包含当前 Web 页面的绘制性能打点信息,可通过 performance.getEntriesByType('paint') 方法获取。
找到 name 为 first-contentful-paint 的对象,描述的即为 FCP 的指标数据,其中 startTime 即为 FCP 时间。
计算 LCP 指标
最大内容绘制 (LCP) 是测量感知加载速度的一个以用户为中心的重要指标,因为该项指标会在页面的主要内容基本加载完成时,在页面加载时间轴中标记出相应的点,迅捷的 LCP 有助于让用户确信页面是有效的。
最大内容绘制 (LCP) 指标会根据页面首次开始加载的时间点来报告可视区域内可见的最大图像或文本块完成渲染的相对时间。
根据当前最大内容绘制 API中的规定,最大内容绘制考量的元素类型为:
<img>
元素- 内嵌在
<svg>
元素内的<image>
元素 <video>
元素(使用封面图像)- 通过
url()
函数(而非使用CSS 渐变)加载的带有背景图像的元素 - 包含文本节点或其他行内级文本元素子元素的块级元素
指标获取实现细节
创建一个 PerformanceObserver ,使用 Largest Contentful Paint API 监听并上报。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}})
.observe({type: 'largest-contentful-paint', buffered: true});
计算FMP指标
FMP(首次有意义内容绘制时间,First Meaningful Paint)是页面的主要内容被渲染到屏幕的时间点,换句话来说,即页面的主要内容何时对用户可见。这是我们衡量网页加载对用户体验的一个重要指标。
基于布局变动趋势衡量
实现原理
指标计算原理主要是基于布局变动趋势衡量 FMP,FMP 并不像 FP、FCP 一样有浏览器直接提供的 API,需要开发者自行研究算法实现,这里介绍一种基于布局变动趋势衡量的算法:基本原理是依照 DOM 的复杂度为当前 DOM 设定一个分数,分析 DOM 分数的变动程度来找出合适的 FMP 时间点,而 DOM 的复杂程度和 DOM 的深度息息相关。
代码实现的理论依据在于:认为DOM 结构变化的时间点与之对应渲染的时间点近似相同。则 FMP 的时间点为 DOM 结构变化最剧烈的时间点。
DOM 结构变化的时间点可以利用 MutationObserver API 来获得。 通过 MutationObserver 监听每一次页面整体的 DOM 变化,触发 MutationObserver 的回调,在回调计算出当前 DOM 树的分数,分数变化最剧烈的时刻,即为 FMP 的时间点。
监控变化
想要对 DOM 节点进行阶段性标记,就得有监听 DOM 变化的能力,庆幸的是,HTML5 赋予了我们这个能力。
MutationObserver,Mutation Events功能的替代品,是DOM3 Events规范的一部分。他可以在指定的 DOM 发生变化时执行回调。
什么时间计算?
window.load 开始计算
为什么?
我们认为,通常情况下,在 window 触发 load 事件的时刻,意味着主要业务的 90% 的资源和 dom 都已经准备就绪。此时算出的高权重得分的 dom 就是我们想要找的 FMP 关键节点。
计算CLS指标
累积布局偏移 (CLS) 是测量视觉稳定性的一个以用户为中心的重要指标,因为该项指标有助于量化用户经历意外布局偏移的频率,较低的 CLS 给用户呈现的效果是交互流程自然、没有延迟和卡顿。
CLS 指标介绍(Cumulative Layout Shift)
CLS 测量整个页面生命周期内发生的所有意外布局偏移量中最大一连串的布局偏移分数。每当一个可见元素的位置从一个已渲染帧变更到下一个已渲染帧时,就发生了布局偏移 。更多信息,请参见布局偏移分数。
CLS 分数如何计算?
浏览器在计算布局偏移分数时,会查看可视区域大小和两个已渲染帧之间的可视区域中不稳定元素的位移。布局偏移分数是该位移的两个度量的乘积:影响分数和距离分数(两者定义如下)。
布局偏移分数 = 影响分数 * 距离分数
影响分数
影响分数测量不稳定元素对两帧之间的可视区域产生的影响。前一帧和当前帧的所有不稳定元素的可见区域集合(占总可视区域的部分)就是当前帧的影响分数。
在上图中,有一个元素在一帧中占据了一半的可视区域。接着,在下一帧中,元素下移了可视区域高度的 25%。红色虚线矩形框表示两帧中元素的可见区域集合,在本示例中,该集合占总可视区域的 75%,因此其影响分数为0.75 。
距离分数
布局偏移分数计算公式的另一部分测量不稳定元素相对于可视区域位移的距离。距离分数指的是任何不稳定元素在一帧中位移的最大距离(水平或垂直)除以可视区域的最大尺寸维度(宽度或高度,以较大者为准)。
在上方的示例中,最大的可视区域尺寸维度是高度,不稳定元素的位移距离为可视区域高度的 25%,因此距离分数为 0.25。 所以,在这个示例中,影响分数是0.75 ,距离分数是0.25 ,所以布局偏移分数是0.75 * 0.25 = 0.1875 。
最初,布局偏移分数只根据影响分数进行计算。引入距离分数是为了避免在大元素发生微小位移的情况下进行过度惩罚的情况。
指标获取实现细节
Layout Shift 是由 Layout Instability API 定义的,当视图中可见的元素在两个帧之间改变其起始位置时,该API就会报告布局偏移项。这些元素被认为是不稳定元素。
创建一个 PerformanceObserver ,使用 Layout Instability API 来监听意外的布局变化。
let clsValue = 0;
let clsEntries = [];
let sessionValue = 0;
let sessionEntries = [];
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Only count layout shifts without recent user input.
if (!entry.hadRecentInput) {
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
// If the entry occurred less than 1 second after the previous entry and
// less than 5 seconds after the first entry in the session, include the
// entry in the current session. Otherwise, start a new session.
if (sessionValue
&& entry.startTime - lastSessionEntry.startTime < 1000
&& entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += entry.value;
sessionEntries.push(entry);
} else {
sessionValue = entry.value;
sessionEntries = [entry];
}
// If the current session value is larger than the current CLS value
// update CLS and the entries contributing to it.
if (sessionValue > clsValue) {
clsValue = sessionValue;
clsEntries = sessionEntries;
// Log the updated value (and its entries) to the console.
console.log('CLS:', clsValue, clsEntries)
}
}
}}).observe({type: 'layout-shift', buffered: true});
计算TTI指标
可交互时间 (TTI) 是测量加载响应度的重要实验室指标。该指标有助于识别看起来具备交互性但实际上并非如此的页面情况。迅捷的 TTI 有助于确保页面的有效性。
TTI(Time To Interactive),即从页面加载开始到页面处于完全可交互状态所花费的时间。页面处于完全可交互状态时,满足以下 3 个条件:
- 页面已经显示有用内容。
- 页面上的可见元素关联的事件响应函数已经完成注册。
- 事件响应函数可以在事件发生后的 50ms 内开始执行。
很多情况下,开发者往往只关注页面渲染相关的指标,如 FP、FCP 等,而忽视了页面的可用性指标。TTI 即是反映页面可用性的重要指标。TTI 值越小,代表用户可以更早地操作页面,用户体验就更好。
如何获取 TTI 指标
用户访问 Web 页面,通常会有两种模式:
- 直接通过服务端路由切换的同步跳转场景
- 通过客户端路由跳转的 SPA 页面切换场景
算法描述
- Long Task :在浏览器主线程执行时间超过 50ms 的 Task。
- 静默窗口期:窗口所对应的时间内没有 Long Task,且进行中的网络请求数不超过 2 个。
- 从起始点(一般选择 FCP 或 FMP)时间开始,向前搜索一个不小于 5s 的静默窗口期。静默窗口期:窗口所对应的时间内没有 Long Task,且进行中的网络请求数不超过 2 个。
- 找到静默窗口期后,从静默窗口期向后搜索到最近的一个 Long Task,Long Task 的结束时间即为 TTI。
- 如果没有找到 Long Task,以起始点时间作为 TTI。
- 如果 2、3 步骤得到的 TTI < DOMContentLoadedEventEnd,以 DOMContentLoadedEventEnd 作为TTI。
计算 FID 和 MPFID 指标
首次输入延迟 (FID) 是测量加载响应度的一个以用户为中心的重要指标,因为该项指标将用户尝试与无响应页面进行交互时的体验进行了量化,低 FID 有助于让用户确信页面是有效的。
什么是 FID 和 MPFID 指标
Long Task 定义:在浏览器主线程执行时间超过 50ms 的 Task。
Input Delay:输入延时,记录用户和页面进行交互操作所花费的时间。例如,从用户点击一个按钮,到浏览器正确处理这个按钮的行为,并反馈给用户所花费的时间。 通常情况下,Input Delay 是因为浏览器主线程在忙于执行其他操作,无暇处理用户的交互操作。
FID( First Input Delay),测量从用户第一次与页面交互,例如当他们单击链接、点按按钮或使用由 JavaScript 驱动的自定义控件,直到浏览器对交互作出响应,并实际能够开始处理事件处理程序所经过的时间。
MPFID(Max Potential First Input Delay),记录在页面加载过程中用户和页面进行首次交互操作可能花费的最长时间。
FID 发生在 FCP 和 TTI 之间,因为这个阶段虽然页面已经显示出部分内容,但尚不具备完全的可交互性。这个阶段用户和页面交互,往往会有较大延迟。如下图所示,浏览器接收到用户输入操作时,主线程正在忙于执行一个 Long Task,只有当这个 Task 执行完成后,浏览器才能响应用户的输入操作。
FID 指标计算
FID 的计算需要用户真实操作页面,可以借助 Event Timing API 进行测量:创建 PerformanceObserver 对象,监听 first-input 事件,监听到 first-input 事件后,利用 Event Timing API,通过事件的开始处理时间,减去事件的发生时间,即为 FID。 因为 FID 的值严重依赖用户触发操作的时机,需要考虑 FID 值的分布曲线。一般情况下,建议采用 95 分位的指标值,能反映最差的用户交互体验对应的 FID 值。
MPFID 指标计算
借助 Long Tasks API 收集页面加载过程中的 Long Task,寻找耗时最长的 Long Task,这个 Long Task 的耗时即为 MPFID。