项目上线后,通常都是都会做埋点,做数据监控、异常监控以及性能监控,本文主要聊聊前端性能监控。重点讲performance
怎么做性能监控。
前端性能监控通常检测某些方面的加载时间,通过得到的加载时间的长短来判断项目某方面的性能怎样,具体是哪些方面呢???我们先来看下页面加载是一个怎样的过程:
页面加载
页面加载的方式有两种,一种是加载完资源文件后通过javascript
动态获取接口数据,然后数据返回渲染内容,这就是前后端分离的项目页面加载方式。另一种就是服务器渲染,同构直出前端页面,这种方式相对前一种方式页面加载的速度要快很多,性能方面相对也就要好些。但是目前大部分的项目都是前后端分离,本文重点描述这种方式。
- 输入网址,回车
- 缓存解析:如果浏览器本地缓存有资源从缓存取资源
- 域名解析:
DNS
解析,将域名解析成IP
,如果缓存中存在,直接丛缓存中取IP
,不用做域名解析 - 发送请求:向服务器发送请求
TCP
连接、三次握手:建立浏览器端和服务器端连接- 服务器接到请求:服务器响应请求
- 数据传输
- 浏览器端拿到数据,解析
html
文件,构建DOM
树,CSSOM
树,js
文件的加载可能会阻塞页面的渲染 - 初始的
html
被完全加载和解析后会触发DOMContentLoaded
事件 CSSOM
树和DOM
树构建完成后会开始生成Render
树,绘制- 在生成
Render
树的过程中,浏览器就开始调用GPU
绘制,合成图层,将内容显示在屏幕上 - 没有文件传输,四次挥手,
TCP
连接断开
上面就是页面加载的基本过程,没有展开讲,如果要展开讲的话,十篇文章也讲不完,在本文中只需要清楚大概的整个过程,后期安排系统的整理上述整个过程的内容,做文字输出。
页面加载整个过程中主要分为:白屏、重定向、DNS
查询、TCP
连接、HTTP
请求、DOM
解析、DOMready
、onload
等,这些也就是我们前端性能监控包括的主要方面。我们需要监控这几个方面的数据,做数据分析,进一步做前端性能优化。例如如何加快首屏加载时间、减少http
请求时间等等。
下面讲讲本文主角,performance
,不是Chrome
开发者工具的Performance
面板,当然Chrome
开发者工具也能做性能监控。
performance
performance
是前端性能监控的API
,可以获取到当前页面中也与性能相关的信息。
我们来看看天猫商城首页通过window.performance
这个API
,获取到的一些信息:
从上面的信息可以看到
window.performance
是一个对象,包含了四个属性:
memory
、
navigation
、
timeOrigin
、
timing
,以及一个事件处理程序
onresourcetimingbufferfull
,我们再来看看这几个分别代表什么?
performance.memory
在Chrome
中添加的一个非标准扩展,memory
这个属性提供了一个可以获取到基本内存使用情况的对象MemoryInfo
performance.memory = {
jsHeapSizeLimit, // 内存大小限制,单位是字节B
totalJSHeapSize, // 可使用的内存大小,单位是字节B
usedJSHeapSize // JS对象占用的内存大小,单位是字节B
}
如果usedJSHeapSize
的值大于totalJSHeapSize
,会出现内存泄露的问题,所以不能大于totalJSHeapSize
的值。
performance.navigation
返回PerformanceNavigation
对象,提供了在指定的时间段发生的操作相关信息,包括页面是加载还是刷新、发生了多少重定向等。
performance.navigation = {
redirectCount: xxx,
type: xxx
}
PerformanceNavigation
对象有两个属性,redirectCount
和type
。
performance.navigation.redirectCount
只读属性,表示达到这个页面之前经过多少次重定向跳转,但是这个接口有同源策略的限制,仅能检测到同源的重定向。
performance.navigation.type
只读属性,有四个返回值:0,1,2,225:
- 0:表示当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在
url
中直接输入地址,相当于常数performance.navigation.TYPE_NAVIGATE
- 1:表示点击刷新页面按钮或者通
过Location.reload()
方法显示的页面,相当于常数performance.navigation.TYPE_RELOAD
- 2:表示页面通过历史记录和前进后退访问的,相当于常数
performance.navigation.TYPE_BACK_FORWARD
- 225:表示任何其他加载方式,相当于常数
performance.navigation.TYPE_RESERVED
performance.timeOrigin
返回性能测量开始的时间的高精度时间戳。
上面的时间就表示开始性能测试的时间。
performance.onresourcetimingbufferfull
一个回调的EventTarget
,当触发resourcetimingbufferfull
事件的时候会被调用。
performance.timing
返回PerformanceTiming
对象,包含了各种与浏览器性能相关的数据,提供了浏览器处理页面的各个阶段的耗时,其整体结构可以参考下图:
PerformanceTiming
对象中的属性都是只读属性,值都是精确到
Unix
毫秒的时间戳:
navigationStart
返回当前浏览器窗口的前一个页面的关闭,发生unload
事件时的时间戳。如果没有前一个页面,则等于fetchStart
属性。
unloadEventStart
返回如果前一个页面与当前页面同域,则返回前一个页面unload
事件发生时的时间戳。如果没有没有前一个页面,或者之前的页面跳转不是在同一个域名内,则返回值为0
unloadEventStart
和unloadEventStart
相对应,返回前一个页面unload
事件绑定的回调函数执行完毕的时间戳。如果没有没有前一个页面,或者之前的页面跳转不是在同一个域名内,则返回值为0
redirectStart
返回第一个http
重定向发生时的时间戳。有跳转并且是同域名内的重定向,否则返回值为0
redirectEnd
返回最后一个http
重定向完成时的时间戳。有跳转并且是同域名内的重定向,否则返回值为0
fetchStart
返回浏览器准备好使用http
请求抓取文档的时间戳,这发生在检查本地缓存之前
domainLookupStart
返回DNS
域名查询开始的时间戳,如果使用了本地缓存(也就是没有做DNS
查询,直接从缓存中取到IP
)或者使用了持久连接,则与fetchStart
值相等
domainLookupEnd
返回DNS
域名查询完成的时间戳,如果使用了本地缓存(也就是没有做DNS
查询,直接从缓存中取到IP
)或者使用了持久连接,则与fetchStart
值相等
connectStart
返回http
(TCP
)开始建立连接的时间戳,如果是持久连接,则与fetchStart
值相等。如果在传输层发生了错误并且重新建立连接,则这里显示的是新建立的连接开始的时间戳
connectEnd
返回http
(TCP
)完成建立连接的时间戳,完成了四次握手,如果是持久连接,则与fetchStar
t值相等。如果在传输层发生了错误并且重新建立连接,则这里显示的是新建立的连接完成的时间戳。连接建立指的是所有握手和认证过程全部结束
secureConnectionStart
返回https
连接开始的时间戳,如果不是安全连接,否则返回值为0
requestStart
返回http
请求读取真实文档开始的时间戳(完成建立连接),包括从本地读取缓存。如果连接错误重连时,这里显示的也是新建立连接的时间戳
responseStart
返回http
开始接收响应的时间戳(获取到第一个字节),包括从本地读取缓存
responseEnd
返回http
响应全部接收完成的时间戳(获取到最后一个字节),包括从本地读取缓存
domLoading
返回开始解析渲染DOM
树的时间戳,此时Document.readyState
变为loading
,并将抛出readystatechange
相关事件
domInteractive
返回完成解析DOM
树的时间戳,Document.readyState
变为interactive
,并将抛出readystatechange
相关事件。这里只是DOM
树解析完成,这时候并没有开始加载网页内的资源
domContentLoadedEventStart
返回DOM
解析完成后,网页内资源加载开始的时间戳。即所有需要被执行的脚本开始被解析了。在DOMContentLoaded
事件抛出前发生
domContentLoadedEventEnd
返回DOM
解析完成后,网页内资源加载完成的时间戳。例如JS
脚本加载执行完成,不论执行顺序。DOMContentLoaded
事件也已经完成
domComplete
返回DOM
解析完成,且资源也准备就绪的时间戳。Document.readyState
变为complete
,并将抛出readystatechange
相关事件
loadEventStart
返回load
事件发送给文档,load
回调函数开始执行的时间戳。如果没有绑定load
事件,返回值为0
loadEventEnd
返回load
事件的回调函数执行完毕的时间戳。如果没有绑定load
事件,返回值为0
上面已经解释了相关属性的含义,通过上面的数据能做很多帮助我们做性能监控的事情:
- 页面加载完成时间:代表了用户等待页面可用的时间
let performance = window.performance;
let t = performance.timing;
let time = t.loadEventEnd - t.navigationStart;
- 解析DOM树结构的时间:判断
DOM
树嵌套情况
let performance = window.performance;
let t = performance.timing;
let time = t.domComplete - t.responseEnd;
- 重定向的时间
let performance = window.performance;
let t = performance.timing;
let time = t.redirectEnd - t.redirectStart;
DNS
查询时间:可做预加载,缓存,减少查询时间
let performance = window.performance;
let t = performance.timing;
let time = t.domainLookupEnd - t.domainLookupStart;
- 白屏时间:读取页面第一个字节的时间
let performance = window.performance;
let t = performance.timing;
let time = t.responseStart - t.navigationStart;
- 内容加载完成的时间
let performance = window.performance;
let t = performance.timing;
let time = t.responseEnd - t.requestStart;
- 执行
onload
回调函数的时间
let performance = window.performance;
let t = performance.timing;
let time = t.loadEventEnd - t.loadEventStart;
DNS
缓存时间
let performance = window.performance;
let t = performance.timing;
let time = t.domainLookupStart - t.fetchStart;
- 卸载页面的时间
let performance = window.performance;
let t = performance.timing;
let time = t.unloadEventEnd - t.unloadEventStart;
TCP
建立连接完成握手的时间
let performance = window.performance;
let t = performance.timing;
let time = t.connectEnd - t.connectStart;
我们可以计算出页面加载的过程中各阶段的耗时,根据时长判断某阶段的性能怎么样,再做进一步的优化处理。这是性能优化很重要的一步,我们需要定位到具体的哪个阶段耗时过长,对症下药。
performance
也提供了一些方法,我们来看看一些常用的方法:
自定义统计方法
performance.mark()
创建一个DOMHighResTimeStamp
保存在资源缓存数据中,可通过performance.getEntries()
等相关接口获取。简单的理解就是可以做标记,也就是“打点”
performance.mark(name);
performance.clearMarks()
用于清除标记,如果不加参数,就表示清除所有标记。
performance.clearMarks(name); // 清除指定标记
performance.clearMarks(); // 清除所有标记
performance.measure()
计算两个mark
之间的时长,创建一个DOMHighResTimeStamp
保存在资源缓存数据中,可通过performance.getEntries()
等相关接口获取。
performance.measure(name, startMark, endMark);
performance.clearMeasures
移除缓存中所有entryType
为measure
的资源数据。
performance.clearMeasures(name); // 清除指定记录间隔数据
performance.clearMeasures(); // 清除所有记录间隔数据
上面的四个API
,可以自定义统计一些数据,例如统计某函数的执行时间。
在Vue
中也有用到,为了追踪组件的性能,在Vue2.X
中全局配置API
有这么个方法:
Vue.config.performance = false;
设置为true
以在浏览器开发工具的性能/时间线面板中启用对组件初始化、编译、渲染和打补丁的性能追踪。只适用于开发模式和支持performance.mark API
的浏览器上。我们入口文件中开启,开启后可以使用Vue Performance Devtool
这个chrom
e插件来查看各组件加载情况:
if (process.env.NODE_ENV !== 'production') {
Vue.config.performance = true;
}
在
Vue
源码中,也是通过
performance.mark
和
performance.measure
来实现的,我们看下具体的源码实现:
// vue/src/core/util/perf.js
import { inBrowser } from './env'
export let mark
export let measure
if (process.env.NODE_ENV !== 'production') { // 环境判断 开发环境执行下面程序
const perf = inBrowser && window.performance // 浏览器环境
/* istanbul ignore if */
if (
perf &&
perf.mark &&
perf.measure &&
perf.clearMarks &&
perf.clearMeasures
) {
mark = tag => perf.mark(tag) // 给定tag打点,做标记
measure = (name, startTag, endTag) => {
perf.measure(name, startTag, endTag) // 计算两个mark之间的时长
perf.clearMarks(startTag) // 清除startTag标记
perf.clearMarks(endTag) // 清除endTag标记
// perf.clearMeasures(name) // 清除指定记录间隔数据
}
}
}
从上面的代码可以看出,尤大大通过mark
和measure
两个函数对performance.mark()
和performance.measure()
进行了封装。我们再来看看在源码中怎么应用的:
// vue/src/core/instance/init.js
import { mark, measure } from '../util/perf'
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function (options?: Object) {
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag) // 开始标记
}
// .... 中间代码省略
// 一系列初始化函数
// initLifecycle(vm)
// ....
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag) // 结束标记
measure(`vue ${vm._name} init`, startTag, endTag) // 计算两个mark之间的时长
}
}
}
上面是Vue
初始化实现的相关程序,上面的打点标记就是为了追踪组件初始化的性能情况,就是在初始化的代码的开头和结尾分别使用mark
函数打上两个标记,然后通过measure
函数对这两个标记点进行性能计算。在编译、渲染和打补丁也做了性能追踪,感兴趣的同学可以看看。
上面是Vue
中对performance
的应用,起初看vue
源码的时候看到这块不是很明白,不影响整体的阅读,没有太关系,最近工作上有用到performance
,想到Vue
好像也用到了,就回头看看Vue
中的应用,现在是整明白了。我们再来看看一个比较简单实例,更好的理解下:
performance.mark('markStart'); // 标记一个开始点
for(let i = 0; i < 10; i++) {
console.log(i);
}
performance.mark('markEnd'); // 标记一个结束点
performance.measure('measureVal', 'markStart', 'markEnd');
let measures = performance.getEntriesByName('measureVal');
let measure = measures[0]
console.log('milliseconds: ', measure.duartion); // 0.8999999990919605
// 清除标记
performance.clearMarks();
performance.clearMeasures();
上面就可以通过提供的API
计算出for循环
的执行时间。这几个API
在性能监控应用比较频繁。
性能资源获取方法
performance.getEntries
获取所有资源请求的时间数据,这个函数返回一个按startTime
排序的对象数组:
从上面可以看出,返回都是资源页面加载的相关数据,很多属性与performance.timing
一样,在这就不再解释了。在这里梳理其他几个重要的属性:
name
: 资源名称,是资源的绝对路径或调用mark方法自定义的名称(例如entryType
为resource
时,name表示资源的路径)。duration
,一个DOMHighResTimeStamp
对象,获取该资源消耗时长。startTime
,一个DOMHighResTimeStamp
对象,开始获取该资源的时间。entryType
:
值 | 该类型对象 | 描述 |
---|---|---|
mark |
PerformanceMark |
通过mark() 方法添加到数组中的对象 |
measure |
PerformanceMeasure |
通过measure() 方法添加到数组中的对象 |
paint |
PerformancePaintTiming |
值为first-paint 首次绘制、first-contentful-paint 首次内容绘制 |
resource |
PerformanceResourceTiming |
所有资源加载时间,用处最多 |
navigation |
PerformanceNavigationTiming |
现除chrome 和Opera 外均不支持,导航相关信息 |
frame |
PerformanceFrameTiming |
现浏览器均未支持 |
initiatorType
,初始化该资源的资源类型:
发起对象 | 值 | 描述 |
---|---|---|
a Element |
link /script /img /iframe 等 |
通过标签形式加载的资源,值是该节点名的小写形式 |
a CSS resourc |
css |
通过css 样式加载的资源,比如background 的url 方式加载资源 |
a XMLHttpRequest object |
xmlhttprequest /fetch |
通过xhr 加载的资源 |
a PerformanceNavigationTiming object |
navigation |
当对象是PerformanceNavigationTiming 时返回 |
performance.getEntriesByName
根据参数name
,type
获取一组当前页面已经加载的资源数据。name
的取值对应到资源数据中的name
字段,type
取值对应到资源数据中的entryType
字段。
let entries = window.performance.getEntriesByName(name, type);
performance.getEntriesByType
根据参数type
获取一组当前页面已经加载的资源数据。type
取值对应到资源数据中的entryType
字段
let entries = window.performance.getEntriesByType(type);
getEntriesByName
和getEntriesByType
可以通过指定参数获取某类型的资源数据,这样我们可以资源数据分类统计,得出各类数据的情况。
上面就是performance
的主要内容了,可以通过这个API
做很多关于性能监控的事情,需要根据自己具体的场景制定合适的方案。熟悉Chrome
开发者工具的同学,也是可以通过performance
面板来做性能监控,那为啥还提供API
来做呢,其实更多的希望通过打点统计数据量,可以通过可视化的方式对数据分析,更直观,进而做下一步操作。
结语
文章如有不正确的地方欢迎各位大佬指正,也希望有幸看到文章的同学也有收获,一起成长!
——本文首发于个人公众号———
最后,欢迎大家关注我的公众号,一起学习交流。
今天的文章应用:前端性能监控performance分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17244.html