最近项目中,需要对移动端的页面性能需要做个提升。在固定安卓环境下,对页面性能各个指标的统计,以此为基准,通过其他技术方案进行优化
由于performance.timing
这个指标慢慢已经被遗弃了,需要使用新的指标performance.getEntriesByType('navigation')[0]
。
performance.timing
与performance.getEntriesByType('navigation')[0]
两个时间统计会有点差异,后者统计的时间都是相对本次请求开始作为起始时间,这点比较重要。
1、PerformanceNavigationTiming
performance.getEntriesByType('navigation')[0]
获取到的对象为 PerformanceNavigationTiming
常用属性 | 含义 |
---|---|
startTime | 返回0的记录 |
domContentLoadedEventEnd | 当前文档的DOMContentLoaded 事件完成后的时间,浏览器已完全加载 HTML,并构建了 DOM 树,但像 <img> 和样式表之类的外部资源可能尚未加载完成 |
domContentLoadedEventStart | 发生DOMContentLoaded之前的时间点 |
domInteractive | 前文档就绪状态设置为interactive |
loadEventEnd | 等于当前文档的load加载事件完成的时间 |
loadEventStart | 等于当前文档的load加载事件前的时间 |
unloadEventEnd | unload事件结束事时间 |
unloadEventStart | unload开始时间 |
fetchStart | 浏览器准备好使用 HTTP 请求获取文档 |
2、常用指标
字段 | 计算方式(都是performance.getEntriesByType('navigation')[0] 的属性值) | 解释 |
---|---|---|
页面卸载时间 | unloadEventEnd - unloadEventStart | 前一个页面卸载,存在unload事件 |
重定向时间 | redirectEnd - redirectStart | 重定向次数过多,容易导致时间消耗过多 |
appCache | domainLookupStart - fetchStart | 缓存耗时 |
DNS查询 | domainLookupEnd - domainLookupStart | DNS解析耗时 |
TCP握手 | connectEnd - connectStart | TCP链接耗时 |
HTTP响应 | responseEnd - requestStart | HTML下载时间 |
DOM解析 | domInteractive - responseEnd | |
资源加载 | loadEventStart - domContentLoadedEventEnd | |
白屏 | responseEnd - fetchStart | |
首屏 | domInteractive - fetchStart | 可交互,粗略计算 |
DOM Ready | domContentLoadedEventEnd - fetchStart | |
页面完全加载 | loadEventStart - fetchStart |
用户指标
字段 | 计算方式 |
---|---|
FP | 首次绘制,是时间线上的第一个“时间点”,它代表浏览器第一次向屏幕传输像素的时间,也就是页面在屏幕上首次发生视觉变化的时间,performance.getEntriesByType('paint')[0] |
FCP | 首次内容绘制,代表浏览器第一次向屏幕绘制 “内容”。注意:只有首次绘制文本、图片(包含背景图)、非白色的canvas或SVG时才被算作FCPperformance.getEntriesByType('paint')[1] |
3、资源计算
由performance.getEntries()
获取各种请求资源所耗时间与数量
const rules =[]
const entries=window.performance.getEntries();
[{name:"JS 资源数量",value:"script"},{name:"CSS 资源数量",value:"css"},{name:"IMG 资源数量",value:"img"},{name:"AJAX 请求数量",value:"xmlhttprequest"},{name:"fetch 请求数量",value:"fetch"}].forEach(item=>{
const currentEntries=entries.filter(ele => ele.initiatorType === item.value);
if (currentEntries&¤tEntries.length){rules.push({target:item.name,count:currentEntries.length,start:Math.min(...currentEntries.map((ele)=>ele.startTime)),end:Math.max(...currentEntries.map((ele) => ele.responseEnd))});
}
});
console.table(rules);
4、耗时精准计算
performance.now()
返回页面初始化到调用该方法时的毫秒数
performance.now()
和Date.now()
区别
- 在于精度,
performance.now()
统计到微秒 - 时间值,
performance.now()
是从页面初始化时间算起,而Date.now()
是相对于1970年1月1日算起 Date.now()
会受系统时间影响(可被人为手动调整),performance.now()
是相对页面初始化时间,匀速递增
因为计算,游戏中sdk初始化,开始等时间就会比较精准
5、数据汇总时间点
一般可选择页面的unload时候触发,有时候可以灵活处理。 如果过早上报,有可能部分数据还未正常统计出来
const rules=[];
//...统计,埋点等
window.addEventListener("unload",()=>{
//发送收集后的数据rules
})
6、精准首屏时间计算
可以利用 MutationObserver 接口提供了监视对 DOM 树所做更改的能力,确认某些元素在我们已知会出现在首屏里,然后对其监视,一旦检测到变化,该时间点就是首屏时间。但是有个缺点,就是要确认那些元素会出现在首屏。