Web/14-前端性能优化/01-前端性能分析工具和指标.md
2021-07-29 11:08:52 +08:00

705 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 01-前端性能分析工具和指标
publish: true
---
<ArticleTopAd></ArticleTopAd>
## 性能指标和优化目标之:加载
性能指标:我们在性能优化过程中可以参考的标准。这些标准都是业界或者前人总结出来的指导性经验。我们可以参考这些指标,去指导我们自己的优化。
### 打开网站的初体验
我们以淘宝网站为例按下F12打开浏览器的调试模式。
![](http://img.smyhvae.com/20210115_1601.png)
上图中,鼠标右键点击“刷新”图标(或者鼠标长按刷新图标,松开鼠标后),会弹出三个选项,我们选择最后一个选项“清空缓存并硬性重新加载”。
补充这三个选项都是在调试模式下按下F12弹出调试面板才会出现的。
浏览器的DevTools初印象
![](https://img.smyhvae.com/20210115_1617.png)
上图中,打开 chrome 调试工具点开「设置」icon下面的四个选项中除了“Group by frame”之外其他的三个选项都可以勾选上。
我们可以看到淘宝网站的一些指标:
- 总资源量是 1.3M。
- DOM加载完成时间DOMContentLoaded511ms。这是一个很关键的指标。
- 其他资源的总加载时间是 1.05秒。
我们再来对比一下京东的:
![](http://img.smyhvae.com/20210116-1357.png)
### 保存快照
network里的信息挺多我们可以将其保存下来留着以后做分析、做对照。
![](http://img.smyhvae.com/20210115-1723.png)
如上图所示,我们可以在 network 的空白处右键选择“Save all as HAR with content”将 network 信息保存为 **HAR**文件格式。
**HAR是一种标准的Web格式用户保存性能测试的结果。里面的数据是json格式。**
我们可以使用第三方的 HAR 分析软件来打开 HAR 文件,比如:
- [Google 提供的 HAR 分析器](https://toolbox.googleapps.com/apps/har_analyzer/?lang=zh-CN)
- Fiddler 抓包工具
注意HAR 文件包含了一些敏感信息:
![](http://img.smyhvae.com/20210115-1733.png)
### 瀑布图 Waterfall
![](http://img.smyhvae.com/20210115_1618.png)
瀑布图可以非常直观地把网站的加载过程,用自上而下的方式表达出来,就像瀑布一样。
瀑布图有两中解读方式:一种是横向看,一种是纵向看。
**1、横向看**
横向看的是具体的资源,每一行代表某个资源的加载信息。里面有一些色块来表达加载的过程,每个块的颜色不同。也就是说资源的下载不是单一的过程,而是经历了很多环节。
为了了解资源的具体加载过程,我们把鼠标悬浮在第一个资源的色块上,可以看见一个详情列表:
![](http://img.smyhvae.com/20210115_1632.png)
1等待
- Queueing排队。浏览器会对资源的请求做优先级排序。
2连接
- DNS LookupDNS域名解析。每个资源都有域名对域名做DNS解析然后找到对应服务器的IP地址。
- initial connection客户端和服务器之间建立TCP连接。
- SSL证书该网站为了保证安全性使用了 https 协议启用了SSL证书。启用之后需要做安全认证SSL协商这个过程也会耗时。到这里位置我们可以看到在请求资源之前有很多的前置步骤。
3请求和响应
- Request sent到这一步真正开始请求资源。
- Waiting**TTFB**):资源从请求到响应,有一个等待的时间。
- Content Download收到响应后资源的下载时间。如果值越大表明下载时间越长。有些同步加载的资源会造成阻塞导致网页的整体加载时间过长让用户等待太久。
**TTFB** 是一个很重要的指标它表示的是请求发出到响应到底要经历多久。TTFB 可以给我们一个很直观的感受,我们网站的请求和响应到底是快还是慢,很大程度上是由 TTFB 决定。
影响 TTFB 的因素是什么呢?比如:
- 后台的处理能力的响应速度。
- 网络状况:是否有网络延迟。
**2、纵向看**:(主要看两点)
1看资源与资源之间的联系如果发生阻塞说明资源可能是串行地按顺序加载。可以**按需要适当调整为并行**。
2看关键的时间节点。Waterfall 中有**两根时间线**:蓝色的线是 DOM 加载完成的时间,红色的线是所有资源加载完成的时间。
## 性能指标和优化目标之:交互
上面的内容讲的是**加载**的性能,还有一个需要关注的性能指标是**交互**。也就是网站加载完成后,用户真正开始使用这个网站过程中的的交互体验。
关于交互体验的性能,我们需要关注的是:
- 交互动作的**响应时间**要短:比如点击按钮后的弹窗、在搜索框里输入关键字后的搜索结果。
- 页面滚动要流畅:可以查看 FPS 帧率。
- 异步请求接口的完成时间要短:比如关注/取关主播的响应、领取红包的操作。
### FPS帧率、FRS
这里首先科普两个概念:
- 刷新率显示器每秒有多少帧画面。大多数显示器的刷新率是60帧/秒即60hz
- 帧率FPSframes per second视频或者动画的内容本身每秒有多少帧。由显卡输出帧率。
上面的两个参数中,不要把「刷新率」和「帧率」弄混了。「刷新率」是屏幕的参数,「帧率」是图像、视频等内容的参数。人眼最终看到的效果,是以最低的参数为准的。
目前市场主流手机和电脑屏幕的刷新率基本都是60Hz即每秒显示60帧画面。也就是说当我们在使用手机的时候本质上是手机在连续播放一张张静态图片每秒播放60张让肉眼误认为眼前的画面在动。
![](http://img.smyhvae.com/20210107_2115.gif)
持续滑动的过程中如果页面输出到显示器的帧率低于60帧/秒,则人眼会感觉卡顿。
那么,在浏览器中如何实时显示内容的 FPS 参数呢打开浏览器的控制台后按住快捷键「Cmd + Shift + P」然后输入 `frame`,选择`Show frames per secondFPS meter`。如下:
![](http://img.smyhvae.com/20210115-1930.png)
![](http://img.smyhvae.com/20210115-2146.png)
**温馨提示**
从 2020年7月起chrome 官方已经取消了 fps参数的显示改为了 [FRS](https://twitter.com/addyosmani/status/1281483292026400768)
![](http://img.smyhvae.com/20210115-2006.png)
FRS参数观察的是丢帧率
![](http://img.smyhvae.com/20210115-2010.png)
Chrome官方给我们提供了下面这个网站用于观察 FPS 效果:
- <http://googlesamples.github.io/web-fundamentals/tools/chrome-devtools/rendering-tools/forcedsync.html>
如果实在想要看fps我们可以借助第三方的 [chrome 插件]()来查看 fps参数。
## 用 RAIL 模型测量性能
RAIL 模型是Google提出的可以量化性能的测量**标准**。我们做性能优化时,要尽可能到这个标准。
在做性能优化的时候,我们需要有人告诉我们:做到多好才算好?有没有一些通用的标准?而 RAIL 模型 可以给我们带来量化的指标。
**RAIL 模型包括四个方面**
![](http://img.smyhvae.com/20210115-2027.png)
- Response响应
- Animation动画
- Idle空闲时间
- load加载
参考链接:
- [[Web翻译]用RAIL模型测量性能](https://juejin.cn/post/6872474167543857165)
- <https://web.dev/rail/>
**RAIL 的目标**
- 让良好的用户体验成为性能优化的目标
接下来,我们再看看看 RAIL 的评估标准。
### 1、响应
**目标**:处理用户发起的响应,应该在 50ms 内完成。
**准则**
- 在50毫秒内处理用户输入事件。这适用于大多数输入如点击按钮、切换表单控件或启动动画。这不适用于触摸拖动或滚动。
- 对于需要超过50毫秒才能完成的操作需要提供反馈。
![](http://img.smyhvae.com/20210115-2039.png)
如上图所示Google经过大量研究发现用户能够接受的最高延时是100ms。所以从用户发起交互请求输入任务前端最好能在100ms内给出反馈。
**但是我们的预算只有50毫秒**。因为应用程序在接收到输入任务的时候不一定会马上着手处理它可能还有其他工作正在进行这意味着当前的输入任务可能需要排队50ms左右。所以我们真正能处理这个请求的时间并没有100ms。
### 2、动画
**目标**在10毫秒或更短的时间内制作出动画中的每一帧。100帧/秒。)
我们知道,当动画的帧率是 >= 60帧/秒 的时候,人眼才不会觉得卡顿。此时的理论值为 1000毫秒/60帧 = 16.6 毫秒/帧。
10毫秒和16毫秒之间隔了6秒。这6秒是什么呢因为浏览器需要大约6毫秒的时间来渲染每一帧所以每一帧的准则建议是10毫秒而不是 16.6毫秒。
假设动画本身是60帧/秒,那么,最终渲染出来的效果可能只有 45帧/秒。
**广义的动画**
动画不仅仅是花哨的UI效果。每一种交互都被认为是动画。比如
- 视觉动画
- 滚动
- 拖动、平移元素、放大图片等。
### 3、空闲时间
**目标**最大化闲置时间增加页面在50毫秒内响应用户输入的几率。
这个空闲时间,是和上面的第一点“响应”是结合在一起的。只有空闲足够多,当用户的交互来的时候,我们才能有足够的时间进行处理。
**准则**
- 利用空闲时间做延迟加载。例如,页面在初始化的时候,尽可能少的加载数据,然后利用空闲时间加载其余部分。
- 在空闲时间内处理任务时间不能超过50毫秒。否则就阻塞了用户做其他的输入请求导致卡顿。
- 如果用户在闲置时间工作期间与页面进行交互,那么这个交互应始终处于最高优先级,并中断闲置时间工作。
### 4、加载
**目标**在5秒或更短的时间内加载页面并可以交互。
**准则**
- 这里的5秒包括加载、解析、渲染并确保用户可以交互。
- 加载的过程中可以使用loading框、进度条、骨架屏等方式缓解用户焦虑。
## 使用Chrome DevTools 分析性能
现在主流的性能测量工具:
- Chrome DevTools开发调试、分析性能。
- Lighthouse 网站整体质量评估。
- WebPageTest给网站提供多个地点的测试以及全面的性能报告。
这一段,我们先来讲一讲 Chrome DevTools 。
大家平时在用 Chrome DevTools 的时候,一般使用来开发调试、查看 DOM、css、接口请求等但其实这个工具非常强大。
### size文件大小分析
![](http://img.smyhvae.com/20210116-0946.png)
可以把size从到小排序看看哪个资源的文件较大。
另外上图中的横线处说明该文件在网络传输的时候会做压缩125kb拿到资源之后再解压还原526kb
### performance性能表现
![](http://img.smyhvae.com/20210116-0959.png)
preformance的两个作用
- Record button记录页面加载、用户交互等全过程直到我们手动点击停止按钮。
- Reload button记录页面从刷新到资源加载完成的过程。会自动停止记录。
参数解读:
- Timing关键的时间节点。
- Main主线程做了哪些任务以及调用关系。
Timing参数中尤其注意看`DCL`DOMContentLoaded即DOM加载完成的时间节点。我们可以通过`Main`参数看看DOM在加载完成之前都做了些什么事情。很有可能就是这些事情导致 `DCL`的时间过晚。
我们可以翻到`Main`里的最后一行(即最终调用的位置),往往这个位置就是我们自己写的代码。
### Diable cache
![](http://img.smyhvae.com/20210116-1014.png)
上图中的`Diable cache`是一个很重要的设置选项。
勾选`Diable cache`
- 不走缓存,相当于页面初次访问。
- 如果你希望改的代码立即生效,就一定要勾选上。
不勾选`Diable cache`
- 走缓存,相当于页面二次、三次访问。
- 很多时候,我们需要关心用户在第二次、第三次访问时候,他的访问速度如何、性能如何、我们设置的缓存有没有生效。此时就不要勾选上。
### 模拟网络情况
![](http://img.smyhvae.com/20210116-1023.png)
模拟网络状况(自定义参数):
![](http://img.smyhvae.com/20210116-1026.png)
### Performance monitor
![](http://img.smyhvae.com/20210116-1032.png)
### 快捷键ESC
按住快捷键ESC会列出其他常用的功能菜单
![](http://img.smyhvae.com/20210116-1028.png)
## 使用LightHouse分析性能
我们之所以使用不同的性能测量工具,是因为他们都有不同的特点。这一段要讲的 lighthouse 既可以帮我们生成简易的测试报告,还可以给出一些针对性的优化建议。后面要讲的 WebPageTest 可以帮我们生成详细的性能测试报告。
我们先来看看 Lighthouse。
### Lighthouse 介绍
![](http://img.smyhvae.com/20210115-1739.png)
lighthouse 是 chrome 浏览器的一个性能测量工具。我们先来看看它的性能指标,至于它具体使用,后续的内容再详细介绍。
淘宝跑分举例:
![](http://img.smyhvae.com/20210115-1758.png)
京东跑分举例:
![](http://img.smyhvae.com/20210115-1759.png)
Lighthouse 跑分里,最重要的两个指标如下:
- **First Contentful Paint白屏时间****从白屏到第一次出现内容的时间。**我们可以看到上面提供了一些加载过程的截图10屏里如果只有1到2屏是白屏说明体验还是可以的。
- **Speed Index**:速度指数。
我们不需要关心这个指数是怎么来的,因为背后涉及一套很复杂的公式,我们暂时只需关注这个数值。
Speed Index 标准为4秒超过4秒算比较慢的我们测的淘宝的 speed index 是0.5s,很快了。但我们要结合网站本身的业务来**权衡**。并不是分数越高性能越高,比如百度这样的网站,页面上的内容很少,测出来的分数肯定很完美。而淘宝需要展示很多内容给用户看。所以,这个指标只是一个指导作用,并不一定能够达到最优的数值。
Lighthouse 的分析结果里,也给出了颜色标注:
- 红色:比较严重的性能问题
- 黄色:需要做适当优化
- 绿色:说明性能表现很好。
另外Lighthouse 还会给出一些优化建议:
- Opportunities:优化建议。
- Diagnostics问题诊断。
- Passed audits表示这部分没有问题。
### 举例确认某个JS 是否必须在首屏加载
就拿B站来举例来看看它的lighthouse报告
![](http://img.smyhvae.com/20210116_0107.png)
上图中给出了一个优化建议有些JS文件不是首屏加载必须的。
![](http://img.smyhvae.com/20210116_0108.png)
我们随便拿一个JS文件来测试比如上图中Header标签里的JS文件。做法如下
![](http://img.smyhvae.com/20210116-0901.png)
如上图所示,在 chrome 控制台输入快捷键「Cmd + Shift + P」然后输入文本`block`,选择`Show Network request blocking`
![](http://img.smyhvae.com/20210116-0903.png)
按照上面的步骤添加规则点击add后效果如下
![](http://img.smyhvae.com/20210116-0904.png)
然后,我们切换到控制台的 network面板并刷新页面
![](http://img.smyhvae.com/20210116-0905.png)
然后观察这个js资源是不是首屏加载所必须的。但我们也不能就此定论说这个资源一定可以延迟加载也许它就是想让页面在一开始loading的时候就捕获日志。
对于我们自己的网站,这个资源是首屏加载必须的吗?一定要在第一时间加载吗?需要根据特定的业务做衡量。
### 通过npm运行 Lighthouse工具
```bash
# 安装
npm install -g lighthouse
# 执行
lighthouse https://www.jd.com
# 输出性能检测报告
Generating results...
html output witten to /Users/smyh/Documents/wpt-mac-agent/www.jd.com._2021-01-16_09-00-00.html
```
## 使用 WebPageTest 评估网站性能
程序员经常说的有句话是“我这儿能打开啊。我这儿不报错呀。”大家应该都懂这个梗这就是为什么我们要借助第三方的测试工具而不仅仅只是自己电脑上访问正常就ok了。
我们需要借助 WebPageTest 这样的第三方测试工具,去模拟各种用户的真实场景。
### WebPageTest 使用
网址:<https://www.webpagetest.org>
![](http://img.smyhvae.com/20210115-2203.png)
WebPageTest 在世界各地提供了非常多的服务器,在每个服务器上部署了不同的浏览器,可以让我们有针对性的做测试。如果你做的是一款国际化网站,那更有必要使用一下了。
我们以JD网站举例
![](http://img.smyhvae.com/20210115-2225.png)
按照上面的选项配置完成后点击右侧的「Start Test」即可开始测试。然后等待
![](http://img.smyhvae.com/20210115-2226.png)
### WebPageTest 报告分析
淘宝网站性能测试报告:
- 2020年6月https://webpagetest.org/result/200616_JK_78eebda338285ffe0c2e154ca5032839/
- 2021年1月https://www.webpagetest.org/result/210115_DiCB_f1344d732760365151755e89765b2d37/
JD网站性能测试报告
- 2021年1月https://www.webpagetest.org/result/210115_DiGT_8d7370e91230b7d077e40b7aafb485a5/
拿到 WebPageTest 报告之后,我们来看看报告里的几个重点指标。
![](http://img.smyhvae.com/20210116_1314.png)
1、摘要里的参数如上图
- First Byte第一个请求的响应时间。可以反映后台的处理能力以及网络回路的情况。
- Start Render从白屏到首次渲染的时间。
- Speed Index速度指数。
- **Total Blocking Time**:页面被阻塞,导致用户不能交互的累计时间。
![](http://img.smyhvae.com/20210116_1315.png)
2、详情里的参数**First View**。
First View展示的是首次访问时总的加载时间。这里面提供的瀑布图比 chrome DevTools里提供的更为详细。
点击进入 First View 的详情之后,可以看到:所有的资源请求,都会在这里列出来。如下:
![](http://img.smyhvae.com/20210116_1316.png)
- page is Interactive页面在加载的过程中大部分时间段用户都是可以交互的。这是非常有用的一个指标。
- Brower Main thread浏览器主线程的占用情况。可以看看空闲的时间多不多。
- CPU UtilizationCPU的使用情况。
- 多张图片的资源请求。
![](http://img.smyhvae.com/20210116_1317.png)
上图中,我们可以看到:多张图片的开始请求时间都是相同的。也就是说,如果让资源做**并行加载**,我们就可以加大地减少加载时间,**最终所消耗的时间就由最大的图片来决定**。这是一个很好的优化技巧,至于具体是怎么实现的,可以自行了解。
另外,我们看到,有一部分的请求,被高亮出来了:
![](http://img.smyhvae.com/20210115-2250.png)
上面这张图的意思是302表示重定向也就是说这个资源已经不在原来请求的位置了需要重定向才能找到真实的位置。这个地方其实可以做一个优化
> 不需要去访问之前的无效的资源,可以直接去访问重定向后的那个资源。
### 局域网部署 WebPageTest 工具
如果我们开发的页面还没有上线公网则无法访问。这个时候我们也想通过WebPageTest看看网站的性能那要怎么办呢
我们可以在局域网部署 WebPageTest 工具,具体方法可自行研究。
## chrome插件PageSpeed Insights
另外google官方也有一个网址https://developers.google.com/speed/pagespeed/insights/?hl=zh-cn
但是这个网站在使用时,经常挂掉。
这个插件是2018年的已经好几年没更新了。大家参考即可。
## 实时动态测量性能的API
Chrome DevTools能够检测各种性能参数其实也是调用了一些性能相关的标准API。我们自己也可以直接在代码里调用这些api。
通过 `performance`对象提供的API我们可以实时的、精细化、自定义测量性能获取相应的参数。也可以把这些性能参数打印到控制台或者实时上报给后台监控系统。
### performance获取常见性能参数
常见性能参数,计算公式如下:
> 时间戳1减去时间戳2得到的差值就是我们想要看到的耗时。
- DNS 解析耗时: domainLookupEnd - domainLookupStart
- TCP 连接耗时: connectEnd - connectStart
- SSL 安全连接耗时: connectEnd - secureConnectionStart
- 网络请求耗时 (TTFB): responseStart - requestStart
- 数据传输耗时: responseEnd - responseStart
- DOM 解析耗时: domInteractive - responseEnd
- 资源加载耗时: loadEventStart - domContentLoadedEventEnd
- First Byte时间: responseStart - domainLookupStart
- 白屏时间: responseEnd - fetchStart
- 首次可交互时间(**TTI**: domInteractive - fetchStart
- DOM Ready 时间: domContentLoadEventEnd - fetchStart
- 页面完全加载时间: loadEventStart - fetchStart
- http 头部大小: transferSize - encodedBodySize
- 重定向次数performance.navigation.redirectCount
- 重定向耗时: redirectEnd - redirectStart
比如说,如果我们想要获取 TTI参数代码可以这样写
```javascript
// 计算一些关键的性能指标
window.addEventListener('load', (event) => {
// Time to Interactive
let timing = performance.getEntriesByType('navigation')[0];
console.log(timing.domInteractive);
console.log(timing.fetchStart);
let diff = timing.domInteractive - timing.fetchStart;
console.log("TTI: " + diff); // 打印 TTI 参数
})
```
### 观察长任务
```javascript
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry)
}
})
observer.observe({entryTypes: ['longtask']})
```
### 页面可见性的状态监听
使用场景举例:
- 比如说,我们正在做一个视频网站(或者游戏页面)。如果用户当前没有在看这个视频,而是切换别的页面了。此时,我们可以对视频做节流等处理,避免造成性能的浪费。等用户再回到当前页面之后,再恢复之前的状态。
- 当设备进入待机模式时(用户按下电源键关闭屏幕),网站想要关闭设备声音。
针对这种场景,我们可以使用`visibilitychange`进行监听:
```javascript
// 见面可见性的状态监听
let vEvent = 'visibilitychange';
if (document.webkitHidden != undefined) {
// webkit prefix detected
vEvent = 'webkitvisibilitychange';
}
function visibilityChanged() {
if (document.hidden || document.webkitHidden) {
console.log("Web page is hidden.")
} else {
console.log("Web page is visible.")
}
}
document.addEventListener(vEvent, visibilityChanged, false);
```
### 网络状况监听
使用场景举例:
- 高清图片按需加载:如果用户的网络条件较好,就加载高清图片资源;如果网络条件不好,就加载文件较小的图片资源。
代码举例:
```javascript
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
var type = connection.effectiveType;
function updateConnectionStatus() {
// type是之前的网络状态connection.effectiveType是当前最新的网络状态
console.log("Connection type changed from " + type + " to " + connection.effectiveType);
type = connection.effectiveType;
}
connection.addEventListener('change', updateConnectionStatus);
```
打印结果举例:
```
Connection type changed from 4g to 3g
```
### 检测元素的可见状态,做曝光埋点
我们可以通过`IntersectionObserver`这个API来检测元素的可见状态
![](http://img.smyhvae.com/20210117_1635.png)
做曝光上报的埋点判断某个DOM或者某个楼层是否出现在视窗中出现了就收集数据上报给服务端。
本质就是要计算某一元素和另一元素(视窗)的相对位置/相对可视状态,然后进行一些操作(一般是上报给服务端)。
参考:
- [前端埋点之曝光实现](https://cnodejs.org/topic/5e0a0edb0696c446bf650dec)
- [点击埋点和曝光卖点的封装](https://github.com/Hugohui/vueTrackSdk)