参考资料
Performance集成的性能指标
每个性能指标都用接口实例PerformanceEntry表示,你可通过Performance.getEntries() 或 (更推荐) 通过 PerformanceObserver,因为观察者通知是异步传递的,因此浏览器可以在空闲时间调度它们,以最大限度地减少它们对性能的影响。
performanceObserver API兼容性较好,在2016-2017开始普遍支持,而PerformanceNavigationTiming兼容性则不太理想,Safari浏览器在2021年才开始支持,所有需要做兼容处理使用performance.timing
比较通用的是以下这些指标
"largest-contentful-paint"记录页面加载过程中最大的绘制。Safari不支持"longtask"记录耗时 50 毫秒或以上的任务。"navigation"记录与导航到页面和页面初始加载相关的指标,返回PerformanceNavigationTiming"paint"记录页面加载过程中渲染的关键时刻,包含首次绘制(FP)和首次内容绘制(FCP),兼容性方面也比较差,FCP Safari浏览器在2021年才开始支持,FP则Safari不支持"resource"记录浏览器获取资源所花的时间。
PerformanceNavigationTiming
除了上面性能指标外,我们还可以通过performance的性能时间指标自己计算相关性能指标,比如DOM Ready时间、TTI首次可交互时间、LOAD时间、服务器的响应时间
- domInteractive HTML 解析完成,DOM 树构建完成
- domContentLoadedEventStart触发 DOMContentLoaded 事件
- domContentLoadedEventEnd DOMContentLoaded 事件处理完成
- domComplete DOM 所有资源加载完成
- loadEventStart所有资源加载完成,触发 load 事件
- loadEventEnd load 事件处理完成,页面完全加载
performance.now() 和 Date.now()区别
performance.now()是页面加载(navigationStart)后经过的毫秒数,是微秒级高精度相对值,而 Date.now()是1970年到现在的时间戳,是绝对值
performance.now()+performance.timeOrigin===Date.now()指标详解
从页面第一次开始加载时记录。测量视口中可见的最大图像或文本块的呈现时间,一般指的是首屏时间
FP和FCP的区别:FCP是首次绘制有效内容的时间点;所以FP会等于或者先于FCP网络加载的指标,像script、css、link等资源的加载时间
值得注意的是流式解析与渲染
浏览器接收 HTML 的过程中,不必等待整个文档加载完成,而是随着数据到达,逐步解析和渲染内容。
在解析和渲染的过程中,遇到css会去下载,并且会阻塞渲染和js的执行、遇到普通的js会阻塞DOM解析
在html开始正式解析之前,还会有一个预加载扫描器。这个步骤不是真正去解析html而是扫描分析文件中是否有外部引用文件,如果有的话就会开始下载。
获取Time兼容写法
const getTiming = () => {
let timing: Timing = {
fetchStart: Date.now(),
};
if (!performance) {
return timing;
}
if ("timing" in performance) {
timing = performance.timing;
} else if ("getEntriesByType" in performance && "timeOrigin" in performance) {
const navigationEntries = window.performance.getEntriesByType("navigation");
// 检查是否有 navigation 条目
if (navigationEntries.length > 0) {
timing = (navigationEntries[0] as PerformanceNavigationTiming); // 类型断言
let validKeys = [
"fetchStart",
"responseEnd",
"domInteractive",
"domContentLoadedEventEnd",
"loadEventStart",
] as const;
let res: Partial<Timing> = {};
// 将相对时间转为绝对时间
validKeys.forEach((key) => {
// @ts-ignore
res[key] = performance.timeOrigin + timing[key];
});
timing={...timing,...res}
}
}
return timing;
};TTI指标
timing.domInteractive - timing.fetchStartFMP计算
通过MutationObserver 监听body节点变化,每变化一次,计算一次body分数,取分数突变最大的那一次为首屏时间。分数计算逻辑是在视口内1+0.5*depth,不在视口内为0
遍历是深度优先遍历、如何子可见,父级不用计算一定可见、最后一个兄弟元素可见、前面兄弟元素也一定可见