SSE是啥?如何使用?

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���

往期年度总结

往期文章

专栏文章

相关推荐
花花鱼2 分钟前
vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法
前端·javascript·elementui
k09336 分钟前
sourceTree回滚版本到某次提交
开发语言·前端·javascript
EricWang135827 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning27 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
时差95331 分钟前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database
web行路人37 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱00139 分钟前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
子非鱼9211 小时前
【Ajax】跨域
javascript·ajax·cors·jsonp
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石1 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙