定义
Performance 接口可以获取到当前页面中与性能相关的信息。它是 High Resolution Time API 的一部分,同时也融合了 Performance Timeline API
、Navigation Timing API
、 User Timing API
和 Resource Timing API
。
注意:除了以下指出的情况外,该接口及其成员在
Web Worker
中可用。此外,还需注意,performance 的创建和衡量都是同一环境下的。即,如果你在主线程(或者其他 worker)中创建了一个 performance,那么它在另外的 worker 线程中是不可用的;反之亦然。
Performance常见名词解释
FP (First Paint) 首次绘制
FCP (First Contentful Paint) 首次内容绘制
LCP (Largest Contentful Paint)
最大内容渲染DCL (DomContentloaded)
FMP(First Meaningful Paint) 首次有效绘制
L (onLoad)
TTI (Time to Interactive) 可交互时间
TBT (Total Blocking Time) 页面阻塞总时长
FID (First Input Delay) 首次输入延迟
CLS (Cumulative Layout Shift) 累积布局偏移
SI (Speed Index)
Performance API
MDN Web docs | 描述 |
---|---|
Resource Timing API | 获取和分析应用资源加载的详细网络计时数据 |
Navigation_timing_API | 提供了可用于衡量一个网站性能的数据 |
Performance | 支持应用程序中客户端的延时测量 |
Performance_Timeline | 提供了可用于衡量一个应用程序的客户端延时的数据 |
User_Timing_API | 允许开发者在浏览器性能时间线中创建针对特定应用的时间戳 |
Frame_Timing_API | 提供有关浏览器事件循环的帧计时数据 |
Network_Information_API | 可以获取到系统的网络连接信息,应用程序可以根据此信息为用户展现不同清晰度的内容 |
Navigation Timing: Processing Model
- Navigation Timing Level 1
- Navigation Timing Level 2
- 上一个文档卸载
- 重定向
- 浏览器准备好使用http抓取文档
- 检查本地缓存
- 查询DNS域名
- TCP建立连接
- HTTP请求、响应
- 渲染DOM树并解析
- 网页开始加载资源
- 准备就绪触发load事件执行回调函数
性能上报
// onload事件触发 - 等待1s - 上报性能
var page = windows.performance.timing
// DNS
dns = page.domainLookupEnd - page.domainLookupStart
// TCP
tcp = page.connectEnd - page.connectStart
// 请求返回时长
connectTime = page.responseEnd - page.requestStart
// 服务端响应时长
responseTime = page.responseEnd - page.responseStart
// DOM分析时长
domAnalysis = page.domComplete - page.domInteractive
// Dom渲染时长
domReady = page.domContentLoadedEventEnd - page.navigationStart
// 页面加载所需的总时长
loadTime = page.loadEventEnd - page.navigationStart
PerformanceNavigation (将废弃)
属性
- redirectCount: 如果有重定向的话,页面通过几次重定向跳转而来
- type
type | 描述 |
---|---|
0 | 即 TYPE_NAVIGATENEXT 正常进入的页面(非刷新、非重定向等) |
1 | 即 TYPE_RELOAD 通过 window.location.reload() 刷新的页面 |
2 | 即 TYPE_BACK_FORWARD 通过浏览器的前进后退按钮进入的页面(历史记录) |
255 | 即 TYPE_UNDEFINED 非以上方式进入的页面 |
方法
- Performance.getEntries()
- Performance.getEntriesByType()
- Performance.getEntriesByName()
- performance.now() 精准计算程序运行时间
mark、measures 计算程序时间
- performance.mark() 标记各种时间戳,进行打点
- performance.measure() 测量
- performance.clearMarks() 清除打点
- performance.clearMeasures() 清除测量数据
function randomFunc (n) {
if (!n) {
// 生成一个随机数
n = ~~(Math.random() * 10000);
}
var nameStart = 'markStart' + n;
var nameEnd = 'markEnd' + n;
// 函数执行前做个标记
window.performance.mark(nameStart);
for (var i = 0; i < n; i++) {
// do nothing
}
// 函数执行后再做个标记
window.performance.mark(nameEnd);
// 然后测量这个两个标记间的时间距离,并保存起来
var name = 'measureRandomFunc' + n;
window.performance.measure(name, nameStart, nameEnd);
}
// 执行三次看看
randomFunc();
randomFunc();
// 指定一个名字
randomFunc(888);
// 看下保存起来的标记 mark
window.performance.getEntriesByType('mark')
// 看下保存起来的测量 measure
window.performance.getEntriesByType('measure');
// 清除指定标记
window.performance.clearMarks('markStart888');
// 清除所有标记
window.performance.clearMarks();
// 清除指定测量
window.performance.clearMeasures('measureRandomFunc');
// 清除所有测量
window.performance.clearMeasures();
白屏、首屏渲染时长
性能指标 | 定义 | 衡量指标 |
---|---|---|
白屏 | 用户第一次可以稳定与页面交互的时间 | FCP |
首屏 | 可视区域内容已基本呈现的时间 | LCP |
/*
* 获取白屏时长
*
*/
function getFirstPaint() {
let firstPaints = {};
if (typeof performance.getEntriesByType === 'function'){
let performanceEntries = performance.getEntriesByType('paint') || [];
performanceEntries.forEach((entry) => {
if (entry.name === 'first-paint') {
firstPaints.firstPaint = entry.startTime;
} else if (entry.name === 'first-contentful-paint') {
firstPaints.firstContentfulPaint = entry.startTime;
}
});
} else {
if (chrome && chrome.loadTimes) {
let loadTimes = window.chrome.loadTimes();
let {firstPaintTime, startLoadTime} = loadTimes;
firstPaints.firstPaint = (firstPaintTime - startLoadTime) * 1000;
} else if (performance.timing && typeof performance.timing.msFirstPaint === 'number') {
let {msFirstPaint, navigationStart} = performance.timing; firstPaints.firstPaint = msFirstPaint - navigationStart;
}
}
return firstPaints;
}
/*
* 获取首屏时长
*/
try {
const po = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1]; // 优先取 renderTime,如果没有则取 loadTime
let lcp = lastEntry.renderTime || lastEntry.loadTime;
window.perfData.push({ 'LCP', lcp });
});
po.observe({type: 'largest-contentful-paint'});
} catch (e) { // Do nothing }
FPS 计算(检测页面是否卡顿)
// FPS检测
(() => {
const limit = 3; // 出现低FPS的连续次数上限
const below = 20; // 可容忍的最低FPS
let count = 0;
let lastTime = performance.now();
let frame = 0;
let lastFameTime = performance.now();
let fps = 0;
const loop = () => {
frame += 1;
const now = performance.now();
const fs = (now - lastFameTime);
lastFameTime = now;
// 1000毫秒的 FPS (不需要也是可以的,看你选择)
fps = Math.round(1000 / fs);
if (now > 1000 + lastTime) {
// 1s 时间段的FPS
fps = Math.round((frame * 1000) / (now - lastTime));
frame = 0;
lastTime = now;
}
if (fps < below) {
count += 1;
if (count >= limit) {
console.log('网页卡顿', `连续${count}次FPS低于${below},当前FPS为${fps}`);
BUS.trigger('fps-low'); // 关闭一些JS动画
}
} else {
count = 0;
}
window.requestAnimationFrame(loop);
};
loop();
})();