SSE简介
sse是一个由后端主动向前端推送数据的单向通讯协议。具体介绍可查看mdn
优点:
- 相比于websocket会更轻量。
- 使用http协议,现在服务端都支持。
- 可自动重连。
缺点:
- 单向连接,前端若要与后端通信需配合其它请求。
- 只能传递文本数据。
使用
通过new一个EventSource创建sse实例:
javascript
let source = new EventSource(url);
source.readyState可以获取当前连接的状态:
0
:表示连接未建立或离线1
:表示连接正常,可以接收数据2
:表示连接关闭且不会重新连接
SSE有三个重要的事件,分别为:
- open(当连接打开时,会执行此回调)
- message(当客户端接收到数据的时候触发)
- error(当通信发生异常的时候,会触发此事件)。
后端实现
这你我们使用express框架简单实现
- 应设置长连接的响应头。
- 返回数据,这里应注意返回的数据格式是固定的。每行数据以
\n
结尾,完结数据以\n\n
结尾。retry
表示重新连接的延迟。id
作为这条数据的唯一标识。 - 监听
close
事件,当客户端使用source.close()关闭连接的时候可以触发此方法。
javascript
app.get("/sse", (req, res) => {
res.set({
'Content-Type': 'text/event-stream', //设定数据类型
'Cache-Control': 'no-cache',// 长链接拒绝缓存
'Connection': 'keep-alive' //设置长链接
});
//持续返回数据
const timer = setInterval(() => {
const data = {
message: `Current time is ${new Date().toLocaleTimeString()}`
};
res.write('retry: 10000\n')
res.write(`id: ${Date.now()}\n`)
res.write(`data: ${JSON.stringify(data)}\n\n`);
}, 1000);
if (!timer) res.write("data: 没有长连接\n\n");
res.socket.on('close', () => {
console.log("长连接关闭了")
clearInterval(timer);
})
})
前端实现
html
// index.html
<button id="btn_start">点击开始连接服务</button>
<button id="btn_stop">关闭连接</button>
<ul id="list"></ul>
<script src="./index.js"></script>
使用 EventSource
创建一个长连接实例,入参为后端长连接url。当接收到消息时,在页面中创建 li
元素,并把获取到的信息展示到页面中。
javascript
// index.js
const btn_start = document.getElementById("btn_start");
const btn_stop = document.getElementById("btn_stop");
//生成li元素
function createLi(data) {
let li = document.createElement("li");
li.innerHTML = String(data.message);
document.getElementById("list").appendChild(li)
}
let source = ''
btn_start.addEventListener("click", function () {
//判断当前浏览器是否支持SSE
if (!window.EventSource) {
alert("当前浏览器不支持SSE")
throw new Error("当前浏览器不支持SSE")
}
source = new EventSource('http://localhost:8088/sse/');
source.onopen = onConnectOpen;
source.onmessage = onConnectMessage;
source.onerror = onConnectError;
});
btn_stop.addEventListener("click", function () {
if (source) source.close();
console.log("长连接关闭");
});
// //对于建立链接的监听
function onConnectOpen(event) {
console.log(source);
console.log("长连接打开", event);
};
//对服务端消息的监听
function onConnectMessage(event) {
console.log(JSON.parse(event.data));
console.log("收到长连接信息", event);
createLi(JSON.parse(event.data));
};
//对断开链接的监听
function onConnectError(event) {
console.log(source, source.readyState);
console.log("长连接中断", event);
};
其他
通过上述案例可以了解SSE的基础知识,当看到返回的仅仅是字符串时,也许会产生"只能用来传一些JSON数据"的感觉,其实大部分时候是这样的。但它也可以用来做大文件分片下载,试想一下:如果后端通过SSE对文件进行分片,然后转成base64的编码传递给前端,前端再把base64转成二进制,通过创建url以Blob为入参,就可以获取到下载这个大文件的url,进而处理相关逻辑。
这样做的有一个缺点,既然是大文件,当前端把所有文件块获取到的时候会占用很大内存,所以下载大文件得用流式下载的方法,最简单的是给 a
标签赋予 url,触发浏览器动作去下载,这样的话后端其实不必使用这种方式处理,所以这个例子只是作为一个可行性的分析,实际中不推荐。
注: 当不使用 HTTP/2时,打开长连接是有数量限制的,最多为6���
往期年度总结
往期文章
- 展示大量数据节点(tree),引发的一次性能排查
- ts装饰器的那点东西
- 这是你所知道的ts类型断言和类型守卫吗?
- TypeScript官网内容解读
- 经常使用ts的你,知道这些内容?
- 你有了解过原生css的scope?
- 现在比较常用的移动端调试你知道哪些?
- 众多跨标签页通信方式,你知道哪些?(二)
- 众多跨标签页通信方式,你知道哪些?
- 反调试吗?如何监听devtools的打开与关闭
- 因为原生,选择一家公司(前端如何防笔试作弊)
- 结合开发,带你熟悉package.json与tsconfig.json配置
- 如何优雅的在项目中使用echarts
- 如何优雅的做项目国际化
- 近三个月的排错,原来的憧憬消失喽
- 带你从0开始了解vue3核心(运行时)
- 带你从0开始了解vue3核心(computed, watch)
- 带你从0开始了解vue3核心(响应式)
- 3w+字的后台管理通用功能解决方案送给你
- 入职之前,狂补技术,4w字的前端技术解决方案送给你(vue3 + vite )