核心原理
- 基于 HTTP 协议,建立「客户端 → 服务器」的单向长连接
- 服务器通过该连接持续向客户端推送数据(支持文本、JSON 等格式)
- 适合服务器单向推送场景(如实时日志、数据看板、股票行情)
使用Nestjs实现SSE
服务启动在3001端口上,结合前端实现对接推送数据,在modules/notify/notify.controller.ts中实现SSE接口:
ts
import { Controller, Get, Post, Body, Patch, Param, Delete, Sse } from '@nestjs/common';
import { NotifyService } from './notify.service';
import { Observable, interval } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
@ApiTags('通知模块')
@Controller('notify')
export class NotifyController {
constructor(private readonly notifyService: NotifyService) {}
@ApiOperation({ summary: 'SSE流式数据' })
@Sse('stream')
stream(): Observable<{ data: string }> {
// 添加 take(100) 限制只发送100次数据,然后自动结束流
return interval(200).pipe(
take(200), // 只发送100次
map((count) => ({ data: `这是第${count + 1}次发送的SSE数据` })),
);
}
}前端对接(Vue3)
vue
<template>
<div class="sse">
<h2>服务器发送事件(SSE)</h2>
<p>机制:服务器单向推送,客户端被动接收</p>
<button @click="toggleSSE">
{{ isConnected ? "断开连接" : "建立连接" }}
</button>
<div class="message-box">
<h3>推送历史:</h3>
<ul>
<li v-for="(item, index) in messageList" :key="index">{{ item }}</li>
</ul>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onUnmounted } from "vue";
const messageList = ref<string[]>(["等待服务器推送..."]);
const isConnected = ref(false);
let eventSource: EventSource | null = null;
// 建立/断开 SSE 连接
const toggleSSE = () => {
if (isConnected.value) {
// 断开连接
eventSource?.close();
eventSource = null;
isConnected.value = false;
messageList.value.push("已断开 SSE 连接");
} else {
// 建立连接(使用 Nest.js 内置 SSE 接口)
eventSource = new EventSource("http://localhost:3001/notify/stream");
// 监听正常推送(type: 'message')
eventSource.onmessage = (event) => {
console.log("event", event.data);
const data = JSON.parse(event.data);
messageList.value.push(data);
};
// 监听自定义事件类型(可选)
eventSource.addEventListener("customEvent", (event) => {
messageList.value.push(`自定义事件:${event.data}`);
});
// 监听连接错误
eventSource.onerror = (error) => {
console.error("SSE 连接错误:", error);
messageList.value.push("SSE 连接异常,已断开");
isConnected.value = false;
eventSource?.close();
};
// 监听连接成功
eventSource.onopen = () => {
isConnected.value = true;
messageList.value.push("已建立 SSE 连接");
};
}
};
// 组件卸载时断开连接
onUnmounted(() => {
eventSource?.close();
});
</script>
<style scoped>
.sse {
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
.message-box {
flex: 1;
min-height: 0;
margin: 20px 0;
padding: 15px;
border: 1px solid #eee;
border-radius: 4px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
ul {
padding: 0 32px;
margin-left: 20px;
flex: 1;
overflow-y: auto;
min-height: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
li {
margin: 5px 0;
}
button {
padding: 8px 16px;
background: #42b983;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 20px;
}
</style>SSE的优缺点对比
优点
- 单向推送,节省客户端资源
- 自动重连(浏览器原生支持)
- 轻量级,无需复杂协议
缺点
- 仅支持服务器 → 客户端单向通信
- 单个连接只能推送文本数据(需序列化)
- 部分代理服务器可能不支持长连接