页面加载时间
阶段计算表
字段 | 描述 | 计算方式 | 意义 |
---|---|---|---|
unload | 前一个页面卸载耗时 | unloadEventEnd – unloadEventStart | – |
redirect | 重定向耗时 | redirectEnd – redirectStart | 重定向的时间 |
appCache | 缓存耗时 | domainLookupStart – fetchStart | 读取缓存的时间 |
dns | DNS解析耗时 | domainLookupEnd – domainLookupStart | 可观察域名解析服务是否正常 |
tcp | TCP连接耗时 | connectEnd – connectStart | 建立连接的耗时 |
ssl | 安全连接耗时 | connectEnd – secureConnectiuonstart | 反映数据安全连接建立耗时 |
TTFB | 网络请求耗时 | responseStart – requestStart | TTFB是发出页面请求到接收数据第一个字节的时间(ms) |
response | 响应数据传输耗时` | responseEnd – resoibseStart | 观察网络是否正常 |
dom | DOM解析耗时 | domInteractive – responseEnd | 观察DOM结构是否合理,是否有JS阻塞页面解析 |
dcl | DOMDOntentLoaded事件耗时 | domContentLoadedEventEnd – domContentLoadedEventStart | 当HTML文档完成加载和解析之后,DOMDOntentLoaded事件等待样式表\图像完成加载的时长 |
resources | 资源加载耗时 | domComplete – domContentLoadedEventEnd | 可观察文档流是否过大 |
domReady | DOM阶段渲染耗时 | rdomContentLoadedEventEnd – fetchStart | DOM和页面资源加载完成时间,会触发 domDContentLoaded 事件 |
首次渲染耗时 | – | responseEnd – fetchStart | 加载文档到看到第一针非空图像的时间,(白屏时间) |
首次可交互时间 | – | domInteractive – fetchStart | DOM树解析完成时间,此时 document.readyState 为 interactive |
首包时间耗时 | – | responseSyary – domainLookupStart | DNS解析到响应返回给浏览器的第一个字节 |
页面完全加载时间 | – | loadEventStart – fetchStart | – |
onLoad | onLoad事件耗时 | loadEventEnd – loadEventSatrt | – |
实现
值得注意的是 performance.timing 这个属性在MDN上标识为废弃属性, 推荐替换属性PerformanceTiming
onload(function(){
var perfEntries = performance.getEntriesByType("navigation");
setTimeout(()=>{
const {
fetchStart,
connectStart,
connectEnd,
requestStart,
responseStart,
responseEnd,
domInteractive,
domContentLoadedEventStart,
domContentLoadedEventEnd,
loadEventStart
} = perfEntries[0];
let log = {
kind:"experience",
type:'timing',
connectTime:connectStart -connectEnd , //连接时间
ttfbTime:responseStart - requestStart, // 首字节到达时间
responseTime:responseEnd - responseStart, // 响应的读取时间
domContentLoadedTime:domContentLoadedEventEnd - domContentLoadedEventStart,// 资源全部加载完成时间
timeToInteractive:domInteractive - fetchStart, // 首次可交互时间
loadTime:loadEventStart - fetchStart, // 完整的加载时间
}
console.log("用户体验监控",log)
},3000)
性能指标
字段 | 描述 | 备注 |
---|---|---|
FP | 首次绘制 | 包括自定义背景,表示的首次将像素绘制到屏幕的时间 |
FCP | 首次内容绘制 | 第一个DOM渲染到屏幕的时间(也可以作为白屏时间) |
FMP | 首次有意义绘制 | 页面有意义的内容绘制时间(浏览器不知道那个元素是有意义的,需要在指定的html中加入 elementtiming属性,值可以随意填写) |
LCP | 最大内容渲染 | 代表在试图中最大的元素的加载时间 |
DCL | DOM加载完成 | HTML加载解析之后,DOMCIntentLoaded时间被触发的时间,无需等待样式表、图像等加载 |
L | onLoad | 当依赖的资源全部被加载完毕之后触发的时间 |
TTI | 可交互时间 | 标记应用及进行渲染并可以相应用户输入的时间 |
FID | 首次输入延迟 | 用户首次和页面交互时页面的响应时间(反射弧时间) |
实现
function timing(){
let FMP,LCP;
// 添加性能条目的观察者
new PerformanceObserver((entryList,observer)=>{
let perfEntries = entryList.getEntries();
FMP = perfEntries[0];
observer.disconnect(); //取消观察
}).observe({entryTypes:['element']}); //观察页面中有意义的元素
new PerformanceObserver((entryList,observer)=>{
let perfEntries = entryList.getEntries();
LCP = perfEntries[0];
observer.disconnect(); //取消观察
}).observe({entryTypes:['largest-contentful-paint']}); // 观察页面最大渲染元素
/**TTI首次延迟 OR FIO处理延迟时间 */
new PerformanceObserver((entryList,observer)=>{
let lastEvent = getLastEvent()
let firstInput = entryList.getEntries()[0];
if(firstInput){
//inputDelay(反射弧时间) = processingStart(开始处理的时间) - startTime(开点击的时间)
let inputDelay = firstInput.processingStart - firstInput.startTime;
let duration = firstInput.duration; // 处理的耗时
if(inputDelay>0 || duration>0){
let log = {
kind:"experience", // 用户体验指标
type:"firstInputDelay", // 首次输入延迟
inputDelay:inputDelay, // 延迟的时间
duration:duration, // 处理的时间
startTime:firstInput.startTime, // 反射弧时间
selector:lastEvent?getSelector(lastEvent.path || lastEvent.target) : "", // 点击的元素
}
console.log("首次交互时长",log)
}
}
observer.disconnect(); //取消观察
}).observe({type:"first-input",buffered:true}); // 观察第一次交互
onload(()=>{
setTimeout(()=>{
let FP = performance.getEntriesByName("first-paint")[0]
let FCP = performance.getEntriesByName("first-contentful-paint")[0];
let paint_log = {
kind:"experience", //用户体验指标,
type:'paint', // 统计每个阶段的时间
firstPaint:FP.startTime, // 首次绘制时间
firstContentPaint:FCP.startTime, // 首次内容绘制时间
firstMeaningfulPaint:FMP.startTime, // 首次意义绘制时间
largestContentfulPaint:LCP.startTime, //最大内容渲染时间
}
console.log("-----性能监控---",paint_log)
},2000)
})
}
上面表格中提到,在获取首次有意义绘制的耗时的时候,需要在HTML中添加一个 elementtiming 属性,属性值可以随意。 示例如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>前端监控SDK</title>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.27.2/axios.js"></script>
</head>
<body>
<div class="container">
<p>测试一下渲染速度</p>
</div>
<script>
setTimeout(()=>{
let content = document.getElementsByClassName("container")[0];
let h1 = document.createElement("h1");
h1.innerHTML = "我为啥是有意义的内容呀,因为我有elementtiming";
h1.setAttribute("elementtiming","xxx")
content.appendChild(h1)
},2000)
</script>
</body>
</html>