Skip to content

Commit

Permalink
Merge pull request #138 from leezozz/feat/add-performance-optimization
Browse files Browse the repository at this point in the history
feat: add performance optimization
  • Loading branch information
leezozz authored Jul 15, 2024
2 parents fb3d150 + 1250b1e commit 758645a
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/.vuepress/src/sideBarData.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ const sideBarConfig = [
title: 'Nuxt',
folder: 'Nuxt',
},
{
title: '性能优化',
folder: 'performance',
},
]
},
{
Expand Down
161 changes: 161 additions & 0 deletions docs/blog/frameSeries/performance/1.基础.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
---
title: 1.基础
date: 2024-07-11
sidebar: auto
tags:
- 性能优化
categories:
- 性能优化
---

## 1.Web性能介绍
Web性能是客观的衡量标准,是用户对加载时间和运行时的直观体验。
- 打开速度
- 动画效果
- 表单提交
- 列表滚动
- 页面切换
...


## 2.优化方案
- 从发出请求到收到响应的优化,比如DNS查询、HTTP长连接、HTTP2、HTTP压缩、HTTP缓存等
- 关键渲染路径优化,比如是否存在不必要的重绘和回流
- 加载过程的优化,比如延迟加载,是否有不需要在首屏展示的非关键信息,占用了页面加载的时间
- 资源优化,比如图片、视屏等不同的格式类型会有不同的使用场景,在使用的过程中是否恰当
- 构件优化,比如压缩合并、基于webpack构件优化方案等
...

## 使用Chrome DevTools测试性能
**网络请求阻止**
- 打开方式:ctrl+shift+p -> show networl request blocking
- 启用网络请求阻止
- 添加组织规则

**Coverage面板** <br />
通过Coverage面板监控并统计出网站应用运行过程中代码执行的覆盖率 <br />
- 打开方式:ctrl+shift+p -> 搜索coverage(覆盖) -> 点击start instrumenting coverage and reload page
统计结果包括:文件的字节大小、执行过程中已覆盖的代码字节数,以及可视化的覆盖率条形图

**Memory面板(内存面板)**
- 打开方式:ctrl+shift+p -> memory(内存)-> 点击显示内存

**Performance面板(性能面板)**

**FPS计数器**<br />
可在页面运行时提供对 FPS 的实时估计。
- 打开方式:ctrl+shift+p -> FPS

**Performance monitor**<br />
包括:CPU 占用率、JavaScript 内存使用大小、内存中挂的 DOM 节点数、JavaScript 事件监听次数及页面发生重绘与重排的处理时间等信息。

## 前端页面的生命周期
问题:在浏览器地址栏输入一个网址到页面渲染出来,中间都经历了什么<br />
1. 输入地址(例如: https://www.example.com),浏览器解析URL(浏览器解析输入的URL,将其分为不同的部分,如协议(https)、域名(www.example.com)、路径(/)、查询参数等)
2. DNS解析(浏览器需要将域名转换为IP地址,这个过程称为DNS解析)
- 浏览器缓存:首先,浏览器会检查其缓存中是否有对应的IP地址
- 操作系统缓存:如果浏览器缓存中没有,浏览器会查询操作系统缓存
- 本地DNS服务器:如果操作系统缓存中也没有,操作系统会查询配置的本地DNS服务器(通常是ISP提供的)
- 递归查询:本地DNS服务器如果没有缓存,会进行递归查询,从根DNS 服务器开始,逐级查询顶级域名服务器、权威DNS服务器,直到找到对应的IP地址
3. 建议TCP链接:TCP三次握手
- SYN:浏览器发送一个 SYN 包到服务器,表示请求建立连接
- SYN-ACK:服务器收到 SYN 包后,返回一个 SYN-ACK 包,表示同意连接
- ACK:浏览器收到 SYN-ACK 包后,发送一个 ACK 包,表示连接建立成功
4. SSL/TLS四次握手(只有https才有这一步)
5. 浏览器发送HTTP/HTTPS请求
- 建立TCP连接后,浏览器发送HTTP或HTTPS请求到服务器。对于HTTPS,还包括建立TLS/SSL加密连接的步骤:
- ClientHello:客户端发送 ClientHello 消息,包含支持的加密算法等信息。
- ServerHello:服务器响应 ServerHello 消息,选择加密算法,并发送服务器证书。
- 证书验证:浏览器验证服务器证书的有效性。
- 密钥交换:客户端和服务器协商对称密钥,用于后续加密通信。
- 握手完成:双方确认加密参数,开始加密通信。
6. 服务器响应请求并返回数据
- 路由处理:根据请求路径,确定处理逻辑。
- 数据库查询:如有需要,查询数据库获取数据。
- 逻辑处理:根据业务逻辑生成响应内容。
- 返回响应:服务器生成响应头和响应体,并发送回浏览器。
7. 浏览器接受响应并解析
- 解析HTTP响应头:获取状态码、内容类型、缓存控制等信息。
- 解析HTML:将HTML文本解析为DOM树。
- 解析CSS:将CSS文本解析为CSSOM树,并应用到DOM树中。
- 解析JavaScript:解析并执行JavaScript代码,可能会操作DOM树和CSSOM树。
- 解析图片、字体等资源:根据HTML和CSS内容,发送请求加载外部资源。
8. 页面渲染
浏览器将DOM树和CSSOM树结合起来,构建渲染树。渲染树包含了所有需要显示的节点及其样式信息

## DNS解析
DNS解析是将人类容易记忆的域名(如www.example.com)转换为计算机可识别的IP(如192.0.2.1)地址。

DNS-prefetch(DNS预获取)是尝试在请求资源之前解析域名。这可能是后面要加载的文件,也可能是用户尝试打开的链接目标。域名解析和内容载入是串行的网络操作,所以这个方式能减少用户的等待时间,提升用户体验。

dns-prefetch 可帮助开发人员掩盖 DNS 解析延迟。 HTML的`<Link>`元素通过dns-prefetch的rel属性值提供此功能
```html
<link rel="dns-prefetch" href="https://fonts.googleapis.com/">
```
注意:
1. dns-prefetch仅对**跨域**域上的DNS查找有效,因此避免使用它来指向您的站点或域。这是因为,到浏览器看到提示时,您站点域背后的IP已经被解析
2. dns-prefetch需慎用,多页面重复DNS预解析会增加重复DNS查询次数
3. 默认情况下浏览器会对页面中和当前域名(正在浏览网页的域名)不在同一个域的域名进行预获取,并且缓存结果,这就是隐式的 DNS Prefetch。如果想对页面中没有出现的域进行预获取,那么就要使用显示 DNS Prefetch 了。

**更多DNS解析优化** <br />
1. 延长DNS缓存时间
2. 尽可能使用A或AAAA记录代替CNAME
3. 使用CDN加速域名
4. 自己搭建DNS服务

**清除DNS缓存** <br />
1. 清除浏览器DNS缓存
- 清除DNS缓存:chrome://net-internals/#dns
- 有时候也需同时清除套接字缓存池:chrome://net-internals/#sockets

2. 清除系统DNS缓存
```js
// 在windows中查看DNS缓存记录
ipconfig /displaydns

// 在windows中清除DNS缓存记录
ipconfig /flushdns

// 在macOS中清除DNS缓存记录
sudo killall -HUP mDNSResponder
```

## HTTP长连接
### 短连接
HTTP协议的初始版本中,每进行一次HTTP通信就要断开一次TCP连接。每次的请求都会造成无谓的TCP连接建立和断开,增加通信的开销。

为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段
```HTTP
Connection: keep-alive
```
这个字段要求服务器不要关闭TCP连接,以便其他请求复用。服务器同样回应这个字段。
```HTTP
Connection: keep-alive
```
一个可复用的TCP连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。

### 长连接
1997年1月,HTTP/1.1版本发布,只比1.0版本晚了半年。它进一步完善了HTTP协议,直到现在还是最流行的版本。 <br />
HTTP 1.1 版的最大变化,就是引入了持久连接(HTTP Persistent Connections),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。 <br />
持久连接的好处在于减少了TCP连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。另外,减少开销的那部分时间,使HTTP请求和响应能够更早的结束,这样Web页面的显示速度也就相应提高了。 <br />
客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。 <br />
目前,对于同一个域名,大多浏览器允许同时建立6个持久连接。

## HTTP2
HTTP/1.1版的头信息肯定是文本(SACII编码),数据体可以是文本,也可以是二进制。HTTP2/则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为“帧”:头信息帧和数据帧。 <br />
HTTP/2是对HTTP/1.x的改进,旨在提高性能、减少延迟,并提供更高效的传输机制。
1. 多工 <br />
HTTP/2复用TCP连接,**在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应**,这样就避免了“队头堵塞”。 <br />
举例来说,在一个TCP连接里面,服务器同时收到了A请求和B请求,于是先回应A请求,结果发现处理过程非常耗时,于是就发送A请求已经处理好的部分, 接着回应B请求,完成后,再发送A请求剩下的部分。 <br />
这样双向的、实时的通信,就叫做多工。 <br />
2. 数据流 <br />
因为HTTP/2的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。 <br />
HTTP/2将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。 <br />
数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。1.1版取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。 <br />
客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。
3. 头信息压缩 <br />
HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如Cookie和 User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。 <br />
HTTP/2对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用gzip 或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
4. 服务器推送 <br />
HTTP/2允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。 <br />
常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。

0 comments on commit 758645a

Please sign in to comment.