1
背景
在用户体验越发重要的今天,关注页面性能、提升页面展现速度及交互体验对前端开发越来越重要。2
历史
说起 PerformanceObserver,我们首先要了解下 Performance Timeline。 Performance Timeline 是 W3C 性能小组提出的一个规范,定义了让开发者在应用整个生命周期内收集各类性能指标的接口。 一般情况下,我们想得到某项性能记录,需要知道指定的性能事件已经发生,比如使用定时轮询的方式,主动调用 performance.getEntries 或者 performance.getEntriesByName 来获取。 为了解决这个问题,在 Performance Timeline Level 2 中,除了扩展了 Performance 的基本定义之外,还增加了 PerformanceObserver 接口,于是最新的 Performance Timeline Level 2 标准中包括了如下三点:1. 扩展了 Performance 接口的基本定义
2. 在 Web Workers 中暴露了 PerformanceEntry
3. 增加了 PerformanceObserver 的支持
此时我们本篇文章的主角:PerformanceObserver 带着它自身的使命终于出现了。3
正文
了解了 PerformanceObserver 出现的背景,接下来我们就重点了解下它的具体用法吧。 3.1 简介 PerformanceObserver 主要用于监测性能度量事件,在浏览器的性能时间轴记录新的 performanceEntry(详见下方介绍) 时会被通知。 通过使用 PerformanceObserver() 构造函数我们可以创建并返回一个新的 PerformanceObserver 对象,从而进行性能的监测。 也就是说,性能指标可以通过 window.performance 获取,但是获取什么时候的指标就可以通过 PerformanceObserver 构造函数生成的实例,在不同的时机拿到对应不同的值。 3.2 优点1. 避免不知道性能事件啥时候会发生,须要重复轮询 timeline 获取记录。
2. 避免产生重复的逻辑去获取不一样的性能数据指标。
3. 避免其余资源需要操作浏览器性能缓冲区时产生竞态关系。
W3C 官网文档鼓励开发人员尽量使用 PerformanceObserver,而不是经过 Performance 获取性能参数及指标。另外,新的性能 API 和指标可能只能经过 PerformanceObserver 接口得到。 3.3 PerformanceEntrykey | 说明 |
connectEnd | HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间 |
connectStart | HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间 |
decodedBodySize | 从HTTP或缓存中获取的消息体积大小 |
domainLookupEnd | DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 |
domainLookupStart | DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 |
duration | 加载时间 |
encodedBodySize | 从HTTP或缓存中获取的body体积大小 |
entryType | 资源类型,entryType类型不同数组中的对象结构也不同 |
fetchStart | 浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存之前 |
initiatorType | 谁发起的请求 |
name | 资源名称,是资源的绝对路径或调用mark方法自定义的名称 |
nextHopProtocol | 获取资源使用的网络协议 |
redirectEnd | 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内的重定向才算,否则值为 0 |
redirectStart | 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0 |
requestStart | HTTP 请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存,连接错误重连时,这里显示的也是新建立连接的时间 |
responseEnd | HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存 |
responseStart | HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存 |
secureConnectionStart | HTTPS 连接开始的时间,如果不是安全连接,则值为 0 |
serverTiming | 包含服务时间元数据的数组 |
startTime | 开始时间 |
transferSize | 加载资源的体积大小,包含请求头及请求体 |
workerStart | DOMHighResTimeStamp |
只有定义没有例子的文章不是一篇合格的技术文,接下来我们就通过示例来了解下 PerformanceObserver 方法的具体用法。
3.5 示例const entryHandler = (list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-paint') {
observer.disconnect()
}
console.log(entry)
}
}
const observer = new PerformanceObserver(entryHandler)
observer.observe({ type: 'paint', buffered: true })
通过以上代码可以得到 FP 的内容:
const entryHandler = (list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
observer.disconnect()
}
console.log(entry)
}
}
const observer = new PerformanceObserver(entryHandler)
observer.observe({ type: 'paint', buffered: true })
通过以上代码可以得到 FCP 的内容:
const entryHandler = (list) => {
if (observer) {
observer.disconnect()
}
for (const entry of list.getEntries()) {
console.log(entry)
}
}
const observer = new PerformanceObserver(entryHandler)
observer.observe({ type: 'largest-contentful-paint\', buffered: true })
通过以上代码可以得到 LCP 的内容:
let cls = 0;
const entryHandler = (list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
cls += entry.value;
console.log('Current CLS value:', cls, entry);
}
}
};
const observer = new PerformanceObserver(entryHandler);
observer.observe({type: 'layout-shift', buffered: true});
通过以上代码得到 CLS 的值,如下:
const entryHandler = (list) => {
for (const entry of list.getEntries()) {
const delay = entry.processingStart - entry.startTime;
console.log('FID :', delay, entry);
}
};
const observer = new PerformanceObserver(entryHandler);
observer.observe({type: 'first-input', buffered: true});
通过运行上方代码可以得到当前页面 FID 的值:
const next = window.requestAnimationFrame ? requestAnimationFrame : setTimeout
['STYLE', 'SCRIPT', 'LINK'] =
observer = new MutationObserver(mutationList => {
const entry = {
children: [],
}
for (const mutation of mutationList) {
if (mutation.addedNodes.length && isInScreen(mutation.target)) {
...
}
}
if (entry.children.length) {
entries.push(entry)
> { =
performance.now() =
})
}
})
{
childList: true,
subtree: true,
})
具体思路大致如下
(1)利用 MutationObserver 监听 document 对象,每当 DOM 元素属性发生变更时,触发事件。
(2)判断该 DOM 元素是否在首屏内,如果在,则在 requestAnimationFrame() 回调函数中调用 performance.now() 获取当前时间,作为它的绘制时间。
(3)将最后一个 DOM 元素的绘制时间和首屏中所有加载的图片时间作对比,将最大值作为首屏渲染时间。
这样就通过 MutationObserver 简单获取了首屏时间。
4
总结
至此,我们的 PerformanceObserver 就基本介绍完成了。PerformanceObserver 这个 API 涉及的内容挺多,本篇文章就针对以下几个内容简单介绍了下: 1、平时我们页面的性能指标数据可以通过 window.performance 获取,但是获取的时机就可以通过 PerformanceObserver 进行监测获取到对应指标。 2、我们可以通过 PerformanceEntry 实例持续获取性能数据,拿到粒度更细的流程的 performance 数据信息。 3、使用 PerformanceObserver 提供的方法 observe() 方法简单实现了 FP、FCP、LCP、CLS、FID 指标数据的获取。 4、最后我们简单实现了通过 MutationObserver 获取 FCP 数据。
本文分享自微信公众号 - 58技术(architects_58)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
|