大文件上传下载
wǎng luò shí huāng 2022-01-26
开发经验
平时开发经验而已
- 如果页面有定时器,那么离开页面前一定要清除定时器,不然隐患很多。
- 写跳转的时候尽量使用 a 元素;处理动态路由的时候,尽量写为:/xxx/xxx/参数 的形式
- 为 seo 做准备
- 前端最能打的本地存储方案
- 参考:https://mp.weixin.qq.com/s/89K-kXD5eZnE36VpCLzfVg
- 文章截图 (opens new window)
- localForage 有一个优雅降级策略,若浏览器不支持 IndexedDB 或 WebSQL,则使用 localStorage。所有接口是异步接口。将数据保存到离线仓库。你可以存储如下类型的 JavaScript 对象:Array、ArrayBuffer、Blob、Float32Array、Float64Array、Int8Array、Int16Array、Int32Array、Number、Object、Uint8Array、Uint8ClampedArray、Uint16Array、Uint32Array、String。参考文档:https://www.npmjs.com/package/localforage。中文文档:https://localforage.docschina.org/。
- 参考:https://mp.weixin.qq.com/s/89K-kXD5eZnE36VpCLzfVg
- localStorage 的封装
- https://juejin.cn/post/7352814673224761371?searchId=20250217112321687BBED467145F40E976
# 实时数据更新
实时数据更新方式:
- 短轮询(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
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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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("连接关闭");
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
:::