# performance
前言
监测的目的是为了确定性能瓶颈,从而有的放矢地开展具体的优化工作。
平时我们比较推崇的性能监测方案主要有两种:可视化方案、可编程方案。
# 1.Performance 面板
Performance 是 Chrome 提供给我们的开发者工具,用于记录和分析我们的应用在运行时的所有活动。它呈现的数据具有实时性、多维度的特点,可以帮助我们很好地定位性能问题。
# 开始记录
右键打开开发者工具,选中我们的 Performance 面板:
当我们选中图中所标示的实心圆按钮,Performance 会开始帮我们记录我们后续的交互操作;当我们选中圆箭头按钮,Performance 会将页面重新加载,计算加载过程中的性能表现。
tips:使用 Performance 工具时,为了规避其它 Chrome 插件对页面的性能影响,我们最好在无痕模式下打开页面:
# 简要分析
这里我打开掘金首页,选中 Performance 面板中的圆箭头,来看一下页面加载过程中的性能表现:
从上到下,依次为概述面板、详情面板。下我们先来观察一下概述面板,了解页面的基本表现:
我们看右上角的三个栏目:FPS、CPU 和 NET。
FPS:这是一个和动画性能密切相关的指标,它表示每一秒的帧数。图中绿色柱状越高表示帧率越高,体验就越流畅。若出现红色块,则代表长时间帧,很可能会出现卡顿。图中以绿色为主,偶尔出现红块,说明网页性能并不糟糕,但仍有可优化的空间。
CPU:表示 CPU 的使用情况,不同的颜色片段代表着消耗 CPU 资源的不同事件类型。这部分的图像和下文详情面板中的 Summary 内容有对应关系,我们可以结合这两者挖掘性能瓶颈。
NET:粗略的展示了各请求的耗时与前后顺序。这个指标一般来说帮助不大。
# 挖掘性能瓶颈
详情面板中的内容有很多。但一般来说,我们会主要去看 Main 栏目下的火焰图和 Summary 提供给我们的饼图——这两者和概述面板中的 CPU 一栏结合,可以帮我们迅速定位性能瓶颈(如下图)。
先看 CPU 图表和 Summary 饼图。CPU 图表中,我们可以根据颜色填充的饱满程度,确定 CPU 的忙闲,进而了解该页面的总的任务量。而 Summary 饼图则以一种直观的方式告诉了我们,哪个类型的任务最耗时(从本例来看是脚本执行过程)。这样我们在优化的时候,就可以抓到“主要矛盾”,进而有的放矢地开展后续的工作了。
再看 Main 提供给我们的火焰图。这个火焰图非常关键,它展示了整个运行时主进程所做的每一件事情(包括加载、脚本运行、渲染、布局、绘制等)。x 轴表示随时间的记录。每个长条就代表一个活动。更宽的条形意味着事件需要更长时间。y 轴表示调用堆栈,我们可以看到事件是相互堆叠的,上层的事件触发了下层的事件。
CPU 图标和 Summary 图都是按照“类型”给我们提供性能信息,而 Main 火焰图则将粒度细化到了每一个函数的调用。到底是从哪个过程开始出问题、是哪个函数拖了后腿、又是哪个事件触发了这个函数,这些具体的、细致的问题都将在 Main 火焰图中得到解答。
# 2.Performance Api
# 访问 performance 对象
performance 是一个全局对象。我们在控制台里输入 window.performance,就可一窥其全貌:
{
"timeOrigin": 1715782552514,
"timing": {
"connectStart": 1715782552518,
"navigationStart": 1715782552514,
"secureConnectionStart": 1715782552706,
"fetchStart": 1715782552514,
"domContentLoadedEventStart": 1715782832720,
"responseStart": 1715782553664,
"domInteractive": 1715782553989,
"domainLookupEnd": 1715782552518,
"responseEnd": 1715782553934,
"redirectStart": 0,
"requestStart": 1715782552860,
"unloadEventEnd": 0,
"unloadEventStart": 0,
"domLoading": 1715782553666,
"domComplete": 1715782842619,
"domainLookupStart": 1715782552518,
"loadEventStart": 1715782842623,
"domContentLoadedEventEnd": 1715782832720,
"loadEventEnd": 1715782842629,
"redirectEnd": 0,
"connectEnd": 1715782552860
},
"navigation": {
"type": 0,
"redirectCount": 0
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 关键时间节点
- 在 performance 的 timing 属性中,我们可以查看到如下的时间戳:
- 这些时间戳与页面整个加载流程中的关键时间节点有着一一对应的关系:
- 通过求两个时间点之间的差值,我们可以得出某个过程花费的时间:
const timing = window.performance.timing # DNS查询耗时
timing.domainLookupEnd - timing.domainLookupStart
timing.connectEnd - timing.connectStart # TCP连接耗时
timing.responseEnd - timing.requestStart # 内容加载耗时
2
3
4
除了这些常见的耗时情况,我们更应该去关注一些关键性能指标:firstbyte、fpt、tti、ready 和 load 时间。这些指标数据与真实的用户体验息息相关,是我们日常业务性能监测中不可或缺的一部分:
timing.responseStart – timing.domainLookupStart # firstbyte:首包时间
timing.responseEnd – timing.fetchStart # fpt:First Paint Time, 首次渲染时间 / 白屏时间
timing.domInteractive – timing.fetchStart # tti:Time to Interact,首次可交互时间
timing.domContentLoaded – timing.fetchStart # ready:HTML 加载完成时间,即 DOM 就位的时间
timing.loadEventStart – timing.fetchStart # load:页面完全加载时间
2
3
4
5
以上这些通过 Performance API 获取到的时间信息都具有较高的准确度。我们可以对此进行一番格式处理之后上报给服务端,也可以基于此去制作相应的统计图表,从而实现更加精准、更加个性化的性能耗时统计。
此外,通过访问 performance 的 memory 属性,我们还可以获取到内存占用相关的数据;通过对 performance 的其它属性方法的灵活运用,我们还可以把它耦合进业务里,实现更加多样化的性能监测需求——灵活,是可编程化方案最大的优点。