这篇文章发布于 2023年08月27日,星期日,16:34,归类于 JS API。 阅读 10565 次, 今日 12 次 5 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=10950 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。
一、Observer家族
没想到Observer家族还有个PerformanceObserver,这就有点匪夷所思,就是,之前我明明好好梳理过浏览器目前支持的Observer,但是都2023年了,突然出现个PerformanceObserver,就非常让人觉得不真实,之前怎么就没看到呢?
MutationObserver可以用来观察DOM元素的变化,ResizeObserver可以用来观察DOM元素尺寸的变化,IntersectionObserver则可以观察元素和浏览器视窗相交的情况。
而本文要介绍的 PerformanceObserver 则可以对页面的各项性能指标进行观察。
二、关于性能 Performance API
Performance API是个庞大的家族,根本学不动的,看看下面这些(参考自MDN文档),知识密度也不是一般的高。
PerformanceObserver是其中非常重要的一部分内容。
三、包含的性能指标
PerformanceObserver中包含如下表所示的一系列性能指标(可以通过 PerformanceObserver.supportedEntryTypes 返回,不同浏览器的支持情况可能会有差异):
指标 | 释义 | |||
---|---|---|---|---|
back-forward-cache-restoration | – | ✔ | ✘ | ✘ |
element | 元素加载时间,实例项是 PerformanceElementTiming 对象。 | ✔ | ✘ | ✘ |
event | 事件延迟,实例项是 PerformanceEventTiming 对象。 | ✔ | ✔ | ✘ |
first-input | 用户第一次与网站交互(即点击链接、点击按钮或使用自定义的JavaScript控件时)到浏览器实际能够响应该交互的时间,称之为First input delay – FID。 | ✔ | ✔ | ✘ |
largest-contentful-paint | 屏幕上触发的最大绘制元素,实例项是 LargestContentfulPaint 对象。 | ✔ | ✘ | ✘ |
layout-shift | 元素移动时候的布局稳定性,实例项是 LayoutShift对象。 | ✔ | ✘ | ✘ |
long-animation-frame | 长动画关键帧。 | ✔ | ✘ | ✘ |
longtask | 长任务实例,归属于 PerformanceLongTaskTiming 对象。 | ✔ | ✘ | ✘ |
mark | 用户自定义的性能标记。实例项是 PerformanceMark 对象。 | ✔ | ✔ | ✔ |
measure | 用户自定义的性能测量。实例项是 PerformanceMeasure 对象。 | ✔ | ✔ | ✔ |
navigation | 页面导航出去的时间,实例项是 PerformancePaintTiming 对象。 | ✔ | ✔ | ✔ |
paint | 页面加载时内容渲染的关键时刻(第一次绘制,第一次有内容的绘制,实例项是 PerformancePaintTiming 对象。 | ✔ | ✔ | ✔ |
resource | 页面中资源的时间信息,实例项是 PerformanceResourceTiming 对象。 | ✔ | ✔ | ✔ |
taskattribution | 长期任务有重大贡献的工作类型,实例项是 TaskAttributionTiming 对象。 | ✘ | ✘ | ✘ |
soft-navigation | – | ✔ | ✘ | ✘ |
visibility-state | 页面可见性状态更改的时间,即选项卡何时从前台更改为后台,反之亦然。实例项是 VisibilityStateEntry 对象。 | ✔ | ✘ | ✘ |
PS:测试浏览器,Chrome 114,Firefox 116 和 Safari 16.5.2.
可以看到,基本上,每一种性能指标都有一个对应的专属对象类型。
其中,有些指标属于核心指标。
Web性能核心指标
- LCP,Largest Contentful Paint,对应largest-contentful-paint类型,是加载性能指标。
- FID,First Input Delay,对应first-input类型,是交互性能指标。
- CLS,Cumulative Layout Shift,对应layout-shift类型,是视觉稳定性指标。
可以重点关注下,至于其他一些时间类型,及其对应的对象,咳咳……内容实在太多了,学不完,实在是学不完,所以,略过,等哪天真正需要了,可以再好好看一看。
四、paint类型与首次渲染时间
下面看下PerformanceObserver中 ‘paint’ 的运行结果。
看下面的代码和实例,在页面头部写入:
const observer = new PerformanceObserver(entryList => { for (const entry of entryList.getEntries()) { console.dir(entry); } }); observer.observe({ entryTypes: ['paint'] });
在Chrome浏览器下,会输出两个条目对象,如下截图所示:
分别是开始渲染和开始有内容(文字或图片所在DOM)渲染。
例如,有内容渲染的数据结构:
{ "name":"first-contentful-paint", "entryType":"paint", "startTime":220.80000001192093, "duration":0 }
Firefox下则只有一个条目,因为支持的类型更少。
其中可参考的指标就是 startTime,表示什么时候内容开始渲染,基本上,可以近似表示首屏内容呈现的时间。
根据我的测试,第一次进入的时候,时间都会比较长,200ms+,无论哪个浏览器都是,然后再刷新就很快了,都是100ms以内,我估计,是因为类似CSS文件这些资源被缓存了(外链CSS可以阻塞渲染),所以更快了。
眼见为实,您可以狠狠地点击这里:PerformanceObserver首屏渲染时间demo
//zxx: Chrome浏览器下paint有多个类型,我们将entryList.getEntries()
替换成(例如)entryList.getEntriesByName('first-contentful-paint')
。
五、看看largest-contentful-paint的运行结果
虽然LCP是重要的性能指标,但是目前仅Chrome浏览器支持。
运行代码和’paint’类似,这里就不展示,感兴趣的可以猛击这里访问:largest-contentful-paint运行测试demo
输出的结果很奇怪。
在我本地(右上角广告图不加载的时候),最大内容渲染元素是<h1>
标题,线上则是右上角的广告图。
以及,再输出的是pre元素,并且是在定时器触发内容更新后显示。
按照我的理解,最大渲染元素不应该是下面的色彩斑斓的底纹大色框吗?
后来我再想,大应该指的是内容,而不是渲染面积。
于是我又塞入了很多文字内容,结果还是一样。
这个元素的选取规则实在令人费解。
于是我又仔细查看了下MDN文档,大致知道原因了。
The LargestContentfulPaint interface provides timing information about the largest image or text paint before user input on a web page.
需要是在页面可交互之前渲染的元素,并且要在视区范围之内(元素所有区域)。
基于这两点,我又重新修改了下页面内容,包括高度减小,内容增多,然后一刷新,哦吼,成了!
开心,又弄明白一个小知识。
六、过去不太方便获取的first-input指标
first-input指的是用户页面首次交互时间,我一开始还觉得是可交互时间,幸好测试了下。
const observer = new PerformanceObserver(entryList => { for (const entry of entryList.getEntries()) { console.dir(entry); } }); observer.observe({ entryTypes: ['first-input'] });
例如,上面的测试代码,在页面刷新后,是没有任何输出的。
只有当你点击页面,console语句才会执行。
这个API倒是可以用来统计页面是否被用户交互过,以及停留时间,也不知道可不可以用来识别是否是机器行为。
以后有机会试一下。
其输出结果示意:
{ "name": "pointerdown", "entryType": "first-input", "startTime": 2953.4000000953674, "duration": 0, "navigationId": 1, "processingStart": 2953.7000002861023, "processingEnd": 2953.7000002861023, "cancelable": true }
其中startTime就是从页面开始可交互,到第一次交互的时间间隔,name是事件类型,点击是pointerdown,键盘访问是keydown。
我测试了下,在PC端,似乎只有点击和键盘方位才能触发,鼠标hover控件元素,以及鼠标滚轮滚动都是不会触发的。
眼见为实,您可以狠狠地点击这里:first-input首次交互时间测试demo
七、接下来是mark和measure
Web中的Performance中性能都是围绕时间线timeline展开的,。
从Chrome,Firefox等浏览器的性能分析工具可以看出:
而需要知道某一段时间线的性能,就需要知道这个时间线的起止点。
这个起止点的标记就叫做mark,期间的性能测量就是measure。
这两个动作对应两个专门的对象接口,前者是PerformanceMark,可以给任意位置命名和添加标记,后者是PerformanceMeasure,是两个标记之间的时间度量。
我们可以借助这两个对象精确测量某些性能指标。
一种方法是使用Performance API,还有就是使用PerformanceObserver。
这里通过一个例子演示下各自的使用。
假设页面上有两个元素,一个id是output,另外一个是footer,则测试代码如下(使用定时器模拟时间间隔):
// mark标记 performance.mark('test-start', { startTime: 0, detail: { htmlElement: 'output' } }); setTimeout(() => { performance.mark('test-end', { startTime: 360, detail: { htmlElement: 'footer' } }); const testMeasure = performance.measure( "test-start", "test-end" ); console.dir(testMeasure); }, 360); const observer = new PerformanceObserver(entryList => { entryList.getEntries().forEach((entry) => { var logMark = ''; var logMeasure = ''; if (entry.entryType === 'mark') { logMark = `${entry.name}的startTime是: ${entry.startTime}`; console.log(logMark); } if (entry.entryType === 'measure') { console.log(logMeasure = `${entry.name}的duration时间是: ${entry.duration}`); } }); }); observer.observe({ entryTypes: ['mark', 'measure'] });
此时,就可以看到执行开始时间,和时间间隔等信息了。
此时的 duration 应该就是两个DOM元素之间的渲染时间了吧。
眼见为实,您可以狠狠地点击这里:mark和measure类型测试demo
相比与Date.now()或者使用performance.now()方法记录时间,使用mark进行标记的位置还可以和开发者工具的性能面板配合使用,方便我看查看详情,有助于我们定位问题,以及理解Web页面的加载。
例如,上面的demo页面中,’test-start’这个标记就可以在事件时间线中看到。
八、好了,打住,该结束了
经过这一波阅读,测试和撰写,我已经大致搞清楚PerformanceObserver的花花草草了……嗯嗯嗯……是这样的,等哪一天遇到页面卡顿,可以用这个排查看看。
至于性能统计与上报也是个不常见的需求。
所以,了解下就好了。
平常那些项目,只要正常开发,以目前的浏览器性能,都会很流畅的。
另外,还是要亲自测试,只靠看文档,很多东西是看不出来的,并且文档很多内容都是缺失的。
OK,马上9月了,是时候拧紧发条了。
好了,内容已经够多的了,就不再多扯淡了。
感谢您的阅读,行为仓促,如有错误,欢迎指正。
比心~
? ? ? ? ?
本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=10950
(本篇完)
- 尝试使用JS IntersectionObserver让标题和导航联动 (0.498)
- HTML elementtiming属性初体验记录 (0.287)
- 检测DOM尺寸变化JS API ResizeObserver简介 (0.211)
- 突发奇想,同步单复选框checked态岂不点击通杀? (0.211)
- CSS届的绘图板CSS Paint API简介 (0.144)
- JS检测CSS属性浏览器是否支持的多种方法 (0.144)
- 研究了下Houdini中的CSS Layout API (0.144)
- 聊聊JS DOM变化的监听检测与应用 (0.115)
- 如何使用JS检测用户是否缩放了页面? (0.096)
- JS之我用单img元素实现了图像resize拉伸效果 (0.096)
- 翻译 - 高性能网站需避免的7个错误 (RANDOM - 0.072)
你的文章深度和广度,以及文案逻辑性真的都很有限
back-forward-cache这个东西我觉得挺有用的,在前进后退的时候能保留之前的js运行状态,对传统多页网页的场景很有优势
是挺有用,可是很多框架内置都不支持(点名Next13)
在我电脑上试了一下,’first-input’ 这个指标在输入点击普通键位的时候也可以触发。我是mac电脑、chrome 116
第一条评论,哈哈