前言
今天中午在领完盒饭,吃饭的时候,正吃着深海鳕鱼片,蘸上番茄酱,那美味,简直无以言表。突然产品急匆匆的跑过来说:“咱们的用户量这一周一直上不去,有没有什么办法能解决一下?”我忽然虎躯一震,想到前端性能优化是个庞杂又琐碎的工作,怯怯的回答道:“能…能吧…”,产品听到‘能’这个字便哼着小曲扬长而去,留下我独自一人,面对着已经变味的深海鳕鱼片…一遍又一遍的想着问题该如何解决…
window.performance.timing
参考文档:PerformanceTiming
PerformanceTiming
接口是为保持向后兼容性而保留的传统接口,并且提供了在加载和使用当前页面期间发生的各种事件和性能计时信息。
可以通过只读属性window.performance.timing
获得实现该接口的一个对象。
PerformanceTiming
共有21个只读属性
,如下:
-
navigationStart
:当前浏览器窗口的前一个网页关闭,发生unload
事件时的Unix
毫秒时间戳。如果没有前一个网页,则等于fetchStart
属性。 -
unloadEventStart
:如果前一个网页与当前网页属于同一个域名,则返回前一个网页的unload
事件发生时的Unix
毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0
。 -
unloadEventEnd
:如果前一个网页与当前网页属于同一个域名,则返回前一个网页unload
事件的回调函数结束时的Unix
毫秒时间戳。如果没有前一个网页,或者之前的网页跳转不是在同一个域名内,则返回值为0
。 -
redirectStart
:返回第一个HTTP
跳转开始时的Unix
毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0
。 -
redirectEnd
:返回最后一个HTTP
跳转结束时(即跳转回应的最后一个字节接受完成时)的Unix
毫秒时间戳。如果没有跳转,或者不是同一个域名内部的跳转,则返回值为0
。 -
fetchStart
:返回浏览器准备使用HTTP请求读取文档时的Unix
毫秒时间戳。该事件在网页查询本地缓存之前发生。 -
domainLookupStart
:返回域名查询开始时的Unix
毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart
属性的值。 -
domainLookupEnd
:返回域名查询结束时的Unix
毫秒时间戳。如果使用持久连接,或者信息是从本地缓存获取的,则返回值等同于fetchStart
属性的值。 -
connectStart
:返回HTTP请求开始向服务器发送时的Unix
毫秒时间戳。如果使用持久连接(persistent connection
),则返回值等同于fetchStart
属性的值。 -
connectEnd
:返回浏览器与服务器之间的连接建立时的Unix
毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart
属性的值。连接建立指的是所有握手和认证过程全部结束。 -
secureConnectionStart
:返回浏览器与服务器开始安全链接的握手时的Unix
毫秒时间戳。如果当前网页不要求安全连接,则返回0
。 -
requestStart
:返回浏览器向服务器发出HTTP
请求时(或开始读取本地缓存时)的Unix
毫秒时间戳。 -
responseStart
:返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix
毫秒时间戳。 -
responseEnd
:返回浏览器从服务器收到(或从本地缓存读取)最后一个字节时(如果在此之前HTTP
连接已经关闭,则返回关闭时)的Unix
毫秒时间戳。 -
domLoading
:返回当前网页DOM
结构开始解析时(即Document.readyState
属性变为“loading
”、相应的readystatechange
事件触发时)的Unix
毫秒时间戳。 -
domInteractive
:返回当前网页DOM
结构结束解析、开始加载内嵌资源时(即Document.readyState
属性变为“interactive
”、相应的readystatechange
事件触发时)的Unix
毫秒时间戳。 -
domContentLoadedEventStart
:返回当前网页DOMContentLoaded
事件发生时(即DOM
结构解析完毕、所有脚本开始运行时)的Unix
毫秒时间戳。 -
domContentLoadedEventEnd
:返回当前网页所有需要执行的脚本执行完成时的Unix
毫秒时间戳。 -
domComplete
:返回当前网页DOM
结构生成时(即Document.readyState
属性变为“complete
”,以及相应的readystatechange
事件发生时)的Unix
毫秒时间戳。 -
loadEventStart
:返回当前网页load
事件的回调函数开始时的Unix
毫秒时间戳。如果该事件还没有发生,返回0
。 -
loadEventEnd
:返回当前网页load
事件的回调函数运行结束时的Unix
毫秒时间戳。如果该事件还没有发生,返回0
。
有了这些属性,我们就可以计算一些需要的事件数据了
// 页面白屏时间打点,放在domloaded事件中,确保所取的事件均有值
documentReady(() => {
const {
domainLookupStart,
domainLookupEnd,
connectStart,
secureConnectionStart,
connectEnd,
requestStart,
responseStart,
responseEnd,
fetchStart
domContentLoadedEventStart,
} = performance.timing
})
// 需要获取的内容
{
// dns耗时
dns: domainLookupEnd - domainLoopupStart,
// tcp连接耗时
tcp: connectEnd - connectStart,
// https请求耗时
tcps: secureConnectionStart && connectEnd - secureConnectionStart,
// http请求耗时
request: responseStart - requestStart,
// http响应耗时
response: responseEnd - responseStart,
// 从start到domContentLoaden总耗时
total: domContentLoadedEventStart - fetchStart,
}
// 计算加载时间
function getPerformanceTiming () {
var performance = window.performance;
if (!performance) {
// 当前浏览器不支持
console.log('你的浏览器不支持 performance 接口');
return;
}
var t = performance.timing;
var times = {};
//【重要】页面加载完成的时间
//【原因】这几乎代表了用户等待页面可用的时间
times.loadPage = t.loadEventEnd - t.navigationStart;
//【重要】解析 DOM 树结构的时间
//【原因】反省下你的 DOM 树嵌套是不是太多了!
times.domReady = t.domComplete - t.responseEnd;
//【重要】重定向的时间
//【原因】拒绝重定向!比如,http://example.com/ 就不该写成 http://example.com
times.redirect = t.redirectEnd - t.redirectStart;
//【重要】DNS 查询时间
//【原因】DNS 预加载做了么?页面内是不是使用了太多不同的域名导致域名查询的时间太长?
// 可使用 HTML5 Prefetch 预查询 DNS ,见:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)
times.lookupDomain = t.domainLookupEnd - t.domainLookupStart;
//【重要】读取页面第一个字节的时间
//【原因】这可以理解为用户拿到你的资源占用的时间,加异地机房了么,加CDN 处理了么?加带宽了么?加 CPU 运算速度了么?
// TTFB 即 Time To First Byte 的意思
// 维基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
times.ttfb = t.responseStart - t.navigationStart;
//【重要】内容加载完成的时间
//【原因】页面内容经过 gzip 压缩了么,静态资源 css/js 等压缩了么?
times.request = t.responseEnd - t.requestStart;
//【重要】执行 onload 回调函数的时间
//【原因】是否太多不必要的操作都放到 onload 回调函数里执行了,考虑过延迟加载、按需加载的策略么?
times.loadEvent = t.loadEventEnd - t.loadEventStart;
// DNS 缓存时间
times.appcache = t.domainLookupStart - t.fetchStart;
// 卸载页面的时间
times.unloadEvent = t.unloadEventEnd - t.unloadEventStart;
// TCP 建立连接完成握手的时间
times.connect = t.connectEnd - t.connectStart;
return times;
}
performance.getEntriesByType
参考文档:performance.getEntriesByType()
浏览器获取网页时,会对网页中每一个对象(脚本文件、样式表、图片文件等等)发出一个HTTP
请求。performance.getEntries
方法以数组形式,返回这些请求的时间统计信息,有多少个请求,返回数组就会有多少个成员。以PerformanceNavigationTiming
对象形式返回
当我们需要统计各个资源的加载耗时(包括一些图标资源的加载耗时)的时候,可以使用performance.getEntriesByType
这个API
来实现
window.addEventListener('load', () => {
performance.getEntriesByType('resource')
.filter(
(item) => item.name.startsWith('https://xxx.gif') // 过滤掉部分内容
)
.forEach((item: any) => {
const {
name,
transferSize,
initialtorType,
duration,
domainLookupStart,
domainLookupEnd,
connectStart,
secureConnectionStart,
connectEnd,
requestStart,
responseStart,
responseEnd,
startTime,
} = item
// 将对应的属性进行上报
{
page_name: pageName,
name,
transferSize,
initiatorType,
duration: Math.round(duration),
dns: Math.round(domainLookupEnd - domainLookupStart),
tcp: Math.round(connectEnd - connectStart),
tcps: Math.round(
secureConnectionStart && connectEnd - secureConnectionStart
),
request: Math.round(
responseStart - requestStart
),
response: Math.round(
responseEnd - (responseStart || startTime)
),
}
})
})
// 网络类型4g/wifi等
network_type: connection.effectiveType || connection.type || 'NaN',
// 网络宽带
network_downlink: connection.downlink || 'NaN',
// 最大下载速度
network_downlinkMax: connection.downlinkMax || 'NaN',
// Round Trip Time
network_rtt: connection.rtt || 'NaN',
performance.now()
获取当前的微秒数,比Date.now()
更精确,微秒是毫秒的1000
倍。Date.now()
输出的是 UNIX
时间,即距离 1970
的时间,而 performance.now()
输出的是相对于 performance.timing.navigationStart
(页面初始化) 的时间。
performance.mark()
意思就是标记,在程序运行中对其进行时间标记。方便我们计算程序的运行耗时。
// 执行前做个标记
window.performance.mark('markStart');
// do ...
window.performance.mark('markEnd');
// 测量两个标记之间的测量距离
window.performance.measure('measure1', 'markStart', 'markEnd');
// 看下保存起来的标记 mark
var marks = window.performance.getEntriesByType('mark');
console.log(marks);
// 看下保存起来的测量 measure
var measure = window.performance.getEntriesByType('measure');
console.log(measure);
// 清除指定标记
window.performance.clearMarks('markStart');
// 清除所有标记
window.performance.clearMarks();
// 清除指定测量
window.performance.clearMeasures('measure1');
// 清除所有测量
window.performance.clearMeasures();
performance.navigation
提供一些用户行为信息
1. performance.navigation.type
该属性返回一个整数值,表示网页的加载来源,可能有以下4种情况:
0:网页通过点击链接、地址栏输入、表单提交、脚本操作等方式加载,相当于常数performance.navigation.TYPE_NAVIGATENEXT。
1:网页通过“重新加载”按钮或者location.reload()方法加载,相当于常数performance.navigation.TYPE_RELOAD。
2:网页通过“前进”或“后退”按钮加载,相当于常数performance.navigation.TYPE_BACK_FORWARD。
255:任何其他来源的加载,相当于常数performance.navigation.TYPE_UNDEFINED。
2 performance.navigation.redirectCount
该属性表示当前网页经过了多少次重定向跳转
总结
以上就是performance
在性能监控方面比较常用的属性,我们可以通过这些数据来判断在哪些方面进行优化。以下就是我们常做的几点优化:
- 将静态资源放入
CDN
。如果条件允许可以进行多域名部署,因为每一个域名在浏览器下有最大请求量限制,比如chrome
为6个,将静态资源放入过个域名下,就可以增加并发量。同时,我们可以将cdn
预期进行预请求<link ref="dns-prefetch" href="//xxx.xx">
- 图片压缩、图片格式转换。在显示情况可以接受的情况下,可以使用1倍图。如果
CDN
支持图片格式转换,在浏览器支持的情况下,可以将图片转化为webp
格式,可以大大减小图片大小。 - 曝光打点。通过页面的不同模块的曝光量,可以知道不同用户对不同模块的使用情况,进而可以协助产品经理优化项目。
- 懒加载。通过懒加载,可以只加载页面看到的资源,未展示的资源,先不请求,从而达到节省流量,也减轻服务器压力的目的。
react
中可以通过react-intersection-observer来实现。 - 缓存数据。我们可以通过
localStorage
等方式,将上一次请求的数据进行缓存,当用户再次打开页面时,可以先展示上一次缓存的数据,避免用户等待。 - 骨架屏。在第一次无缓存数据时,可以展示骨架屏,避免用户等待时展示白屏。
今天的文章前端性能监控之 performance分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/19597.html