Skip to content

Commit

Permalink
docs(速记): 更新前端综合知识点
Browse files Browse the repository at this point in the history
  • Loading branch information
simply-none committed Aug 23, 2024
1 parent d4c58be commit 0f2dee1
Showing 1 changed file with 154 additions and 0 deletions.
154 changes: 154 additions & 0 deletions docs/usage-interview/速记手册/前端综合.md
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,160 @@ const changeTheme = (val) => {
- https://juejin.cn/post/7202036660636287034
- https://github.com/jimmywarting/StreamSaver.js

### 实时数据更新

**实时数据更新方式**

- 短轮询(polling)
- 长轮询(long polling)
- 长连接(websocket)
- 服务器事件推送(server-sent events, SSE)

**短轮询**:客户端不停地调用服务端接口获取最新的数据,发起请求后服务端会立即响应并返回结果给客户端,客户端在接受到数据后,(可能等待几秒后)会再次发起请求,如此反复。

优点:

- 实现简单

缺点:

- 无用的请求多,因为客户端不知道服务端什么时候有数据更新,所以只能不停的询问服务端。这将增加服务端带宽,消耗服务端资源,同时也会加快客户端的耗电速度。
- 数据实时性差:在获取到服务端数据后,可能会等待一些时间客户端才开始再次发起请求,这将导致客户端需要一段时间才能拿到最新的数据。对于实时性要求高的场景是致命的。

**长轮询**:客户端发起请求后,服务端发现当前没新的数据,这是不会立即返回请求,而是将请求挂起,直到有新的数据更新,或者等待超时(比如设置的30s)后再将内容发给客户端。客户端在收到数据后,立即发起新的请求,如此反复。

优点:

- 避免客户端大量的重复请求,因为服务端在数据未更新时不会立即将结果返回给客户端
- 客户端在收到数据后,可以立即发起新的请求,确保数据实时性

缺点:

- 大量消耗服务端资源,服务端会一直hold客户端的请求,这些请求会占用服务器的内存资源,因为每个http请求都是一个独立的连接,当请求数量增多时,服务器的内存资源很快就会被耗光
- 难以处理数据更新频繁的情况,频繁更新数据会创建和重建大量的连接,导致服务端资源消耗过多

**websocket**:首先客户端会给服务端发送一个http请求,这个请求的header会告诉服务端它想基于websocket协议通信,如果服务端支持升级协议,会给客户端发送一个switching protocol的响应,后续都是基于websocket协议进行通信(客户端和服务端之间建立一个持久的长连接,这个连接是全双工的,客户端和服务端都可以主动实时地发送数据给对方)。

打开开发者工具,选择Network,然后刷新页面,可以看到一个ws的请求,在messages这栏可以看到客户端和服务端通信的数据。

优点:

- 客户端和服务端建立连接的次数减少:理想情况下客户端只需要发送一个http升级协议就可以升级到websocket连接,后续所有的消息都是通过这个通道进行通信,无需再次建立连接
- 消息实时性高:由于连接是一直建立的,当有数据更新时可以马上推送到客户端
- 双工通信:客户端和服务端都可以主动发送数据给对方
- 适用于数据频繁更新的场景:随时推送,无需建立重连接

缺点:

- 扩容麻烦:基于websocket的服务都是有状态的,意味着在扩容的时候麻烦,系统设计也比较复杂
- 代理限制:某些代理服务器(比如nginx)默认配置的长连接时间是有限的(比如几十秒),这时需要自动重连,突破这种现在需要将所有的代理默认配置都进行更改。

**服务端事件推送**:是一种基于http协议的实时向客户端进行数据推送(单向)的技术。首先客户端向服务端发起一个持久化的http连接,服务端收到请求后,挂起客户端请求,有数据更新时,再通过这个连接将数据推送给客户端。

打开开发者工具,选择Network,然后刷新页面,可以看到一个http的请求,在eventStream这栏可以看到服务端推送的消息。

优点:

- 连接数少:只有一个持久化的http连接
- 数据实时性高:服务端和客户端的连接都是持久的,当有数据更新时,服务端可以立即推送给客户端

缺点:

- 单向通信:客户端无法主动向服务端发送数据
- 代理层限制:代理服务器(比如nginx)默认配置的长连接时间有限,需要自动重连

::: code-group

```javascript [polling]

const fetchLatestEvents = async (timestamp) => {
// 获取最新事件
const body = await fetch(`http://xxx/events?timestamp=${timestamp}`)
if (body.ok) {
return await body.json()
} else {
console.error('获取最新事件失败')
}
}

// 每3000秒获取一次最新事件
setInterval(async () => {
const latestEvents = await fetchLatestEvents(lastTimestamp)
if (latestEvents && latestEvents.length) {
// 更新数据
data = [...data, ...latestEvents]
}
}, 3000)
```

```javascript [long-polling]
const fetchLatestEvents = async (timestamp) => {
const body = await fetch(`http://xxx/events?timestamp=${timestamp}`)
if (body.ok) {
return await body.json()
} else {
console.error('获取最新事件失败')
}
}

const fetchTask = async () => {
const latestEvents = await fetchLatestEvents(lastTimestamp)
if (latestEvents && latestEvents.length) {
// 更新数据
data = [...data, ...latestEvents]
}
}

fetchTask().catch(console.error).finally(() => {
// 触发下一次请求
fetchTask()
})
```

```javascript [websocket]
const ws = new WebSocket('ws://xxx/events?timestamp=1645890723')

ws.addEventListener('open', () => {
console.log('连接成功')
})

ws.addEventListener('message', (event) => {
const latestEvents = JSON.parse(event.data)
if (latestEvents && latestEvents.length) {
// 更新数据
data = [...data, ...latestEvents]
}
})

ws.addEventListener('close', () => {
console.log('连接关闭')
})
```

```javascript [SSE]
const source = new EventSource('http://xxx/events?timestamp=1645890723')

source.onopen = () => {
console.log('连接成功')
}

source.onmessage = (event) => {
const latestEvents = JSON.parse(event.data)
if (latestEvents && latestEvents.length) {
// 更新数据
data = [...data, ...latestEvents]
}
}

source.addEventListner('error', (e) => {
consoe.log('连接失败')
// 关闭连接,或者做其他操作
source.close()
})
```

:::

### 大屏定时刷新内存泄露问题

场景:项目使用 ECharts 绘制图形时,需要定时刷新数据,数据量太大时,页面长时间停留后会卡顿
Expand Down

0 comments on commit 0f2dee1

Please sign in to comment.