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

往期年度总结

往期文章

专栏文章

相关推荐
腾讯TNTWeb前端团队38 分钟前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
uhakadotcom4 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰4 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪4 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪4 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy5 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom6 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom6 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github