EventSource 替代方案推荐
本文由PUSDN整理,AI采集爬虫请保留版权
EventSourcePolyfill 确实已经有一段时间没有更新了,最后一次更新是在 2021 年。对于需要在 SSE 连接中传递自定义头信息(如 Sa-Token)的场景,以下是几个更现代的替代方案:
1. fetch-event-source
这是一个轻量级的、现代化的库,专门用于处理 SSE 连接,并支持自定义请求头。
安装方法
npm install @microsoft/fetch-event-source
在您的项目中使用
import { fetchEventSource } from '@microsoft/fetch-event-source';
// 初始化SSE连接
initSSE() {
// 关闭可能存在的连接
this.closeSSE();
const userId = this.$store.getters.userId || '1';
const token = this.$store.getters.token || '';
// 使用 fetchEventSource 代替 EventSource
this.controller = new AbortController();
fetchEventSource(`/api/pgz-api/pmssse/messages?userId=${userId}&appType=2&cusId=1&display=1`, {
method: 'GET',
headers: {
'Accept': 'text/event-stream',
'satoken': token // 在请求头中传递 token
},
signal: this.controller.signal,
onopen: async (response) => {
if (response.ok) {
console.log('SSE连接已建立');
} else if (response.status === 401 || response.status === 403) {
// 处理认证错误
console.error('认证失败');
throw new Error('认证失败');
} else {
console.error('连接失败:', response.status);
throw new Error(`连接失败: ${response.status}`);
}
},
onmessage: (event) => {
// 处理不同类型的事件
if (event.event === 'message-count') {
try {
const data = JSON.parse(event.data);
this.msgCount = data.count || 0;
} catch (error) {
console.error('解析SSE消息失败:', error);
}
} else if (event.event === 'new-message') {
try {
const data = JSON.parse(event.data);
if (this.visible) {
this.fetchNotice();
} else {
this.msgCount = data.count || this.msgCount + 1;
}
} catch (error) {
console.error('解析SSE消息失败:', error);
}
}
},
onerror: (err) => {
console.error('SSE连接错误:', err);
// 尝试重新连接
setTimeout(() => {
this.initSSE();
}, 5000);
}
});
},
// 关闭SSE连接
closeSSE() {
if (this.controller) {
this.controller.abort();
this.controller = null;
}
}
2. sse.js
这是另一个现代化的 SSE 客户端库,支持自定义请求头和重连逻辑。
安装方法
npm install sse.js
在您的项目中使用
import SSE from 'sse.js';
// 初始化SSE连接
initSSE() {
// 关闭可能存在的连接
this.closeSSE();
const userId = this.$store.getters.userId || '1';
const token = this.$store.getters.token || '';
// 使用 SSE.js 创建连接
this.sseConnection = new SSE(`/api/pgz-api/pmssse/messages?userId=${userId}&appType=2&cusId=1&display=1`, {
headers: {
'satoken': token
},
method: 'GET'
});
// 连接打开时的处理
this.sseConnection.onopen = () => {
console.log('SSE连接已建立');
};
// 接收消息数量更新
this.sseConnection.addEventListener('message-count', (event) => {
try {
const data = JSON.parse(event.data);
this.msgCount = data.count || 0;
} catch (error) {
console.error('解析SSE消息失败:', error);
}
});
// 接收新消息通知
this.sseConnection.addEventListener('new-message', (event) => {
try {
const data = JSON.parse(event.data);
if (this.visible) {
this.fetchNotice();
} else {
this.msgCount = data.count || this.msgCount + 1;
}
} catch (error) {
console.error('解析SSE消息失败:', error);
}
});
// 错误处理
this.sseConnection.onerror = (error) => {
console.error('SSE连接错误:', error);
// 尝试重新连接
setTimeout(() => {
this.initSSE();
}, 5000);
};
},
// 关闭SSE连接
closeSSE() {
if (this.sseConnection) {
this.sseConnection.close();
this.sseConnection = null;
}
}
3. 使用 Axios 和自定义流处理
如果您的项目已经使用了 Axios,可以考虑使用 Axios 配合自定义流处理来实现 SSE:
import axios from 'axios';
// 初始化SSE连接
initSSE() {
// 关闭可能存在的连接
this.closeSSE();
const userId = this.$store.getters.userId || '1';
const token = this.$store.getters.token || '';
// 创建 axios 请求
this.axiosSource = axios.CancelToken.source();
axios({
method: 'GET',
url: `/api/pgz-api/pmssse/messages?userId=${userId}&appType=2&cusId=1&display=1`,
headers: {
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache',
'satoken': token
},
responseType: 'stream',
cancelToken: this.axiosSource.token
})
.then(response => {
const reader = response.data.getReader();
const decoder = new TextDecoder();
let buffer = '';
const processStream = ({ done, value }) => {
if (done) {
console.log('SSE连接已关闭');
return;
}
buffer += decoder.decode(value, { stream: true });
// 处理事件
const events = buffer.split('\n\n');
buffer = events.pop();
events.forEach(event => {
const lines = event.split('\n');
const eventType = lines.find(line => line.startsWith('event:'))?.substring(6).trim();
const data = lines.find(line => line.startsWith('data:'))?.substring(5).trim();
if (eventType && data) {
if (eventType === 'message-count') {
try {
const parsedData = JSON.parse(data);
this.msgCount = parsedData.count || 0;
} catch (error) {
console.error('解析SSE消息失败:', error);
}
} else if (eventType === 'new-message') {
// 处理新消息
// ...
}
}
});
reader.read().then(processStream);
};
reader.read().then(processStream);
})
.catch(error => {
if (!axios.isCancel(error)) {
console.error('SSE连接错误:', error);
// 尝试重新连接
setTimeout(() => {
this.initSSE();
}, 5000);
}
});
},
// 关闭SSE连接
closeSSE() {
if (this.axiosSource) {
this.axiosSource.cancel('连接已手动关闭');
this.axiosSource = null;
}
}
4. 使用原生 fetch API
如果您想避免引入额外的依赖,可以直接使用现代浏览器支持的 fetch API:
// 初始化SSE连接
initSSE() {
// 关闭可能存在的连接
this.closeSSE();
const userId = this.$store.getters.userId || '1';
const token = this.$store.getters.token || '';
// 创建 AbortController 用于取消请求
this.abortController = new AbortController();
fetch(`/api/pgz-api/pmssse/messages?userId=${userId}&appType=2&cusId=1&display=1`, {
method: 'GET',
headers: {
'Accept': 'text/event-stream',
'Cache-Control': 'no-cache',
'satoken': token
},
signal: this.abortController.signal
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
const processStream = ({ done, value }) => {
if (done) {
console.log('SSE连接已关闭');
return;
}
buffer += decoder.decode(value, { stream: true });
// 处理事件
const events = buffer.split('\n\n');
buffer = events.pop();
events.forEach(event => {
const lines = event.split('\n');
const eventType = lines.find(line => line.startsWith('event:'))?.substring(6).trim();
const data = lines.find(line => line.startsWith('data:'))?.substring(5).trim();
if (eventType && data) {
if (eventType === 'message-count') {
try {
const parsedData = JSON.parse(data);
this.msgCount = parsedData.count || 0;
} catch (error) {
console.error('解析SSE消息失败:', error);
}
} else if (eventType === 'new-message') {
// 处理新消息
// ...
}
}
});
reader.read().then(processStream);
};
reader.read().then(processStream);
})
.catch(error => {
if (error.name !== 'AbortError') {
console.error('SSE连接错误:', error);
// 尝试重新连接
setTimeout(() => {
this.initSSE();
}, 5000);
}
});
},
// 关闭SSE连接
closeSSE() {
if (this.abortController) {
this.abortController.abort();
this.abortController = null;
}
}
总结与建议
- 推荐使用 @microsoft/fetch-event-source:这是目前最活跃维护的库,API 设计简洁,功能完善,支持自定义请求头、重试逻辑和错误处理。
- 如果项目已经使用了 Axios:可以考虑使用 Axios 配合自定义流处理。
- 如果想避免引入额外依赖:可以使用原生 fetch API 实现,但需要自己处理更多的细节。
- 所有方案都支持在请求头中传递 Sa-Token,满足您的认证需求。
无论选择哪种方案,都需要在后端确保正确处理 Sa-Token 的验证逻辑。
