microsoft/fetch-event-source 是一个由微软开发的开源库,旨在提供比浏览器原生 EventSource API 更强大的 Server-Sent Events (SSE) 请求处理能力
核心优势
- 完全兼容 Fetch API:支持所有 HTTP 方法和自定义头部
- 智能重试控制:开发者可以完全控制连接中断时的重试策略
- 灵活的错误处理:提供完善的错误处理机制
一、安装依赖
javascript
npm install @microsoft/fetch-event-source
二、基本使用
javascript
import { fetchEventSource } from '@microsoft/fetch-event-source';
await fetchEventSource('/api/sse-endpoint', {
method: 'GET', // 支持 GET/POST/PUT 等 HTTP 方法
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token'
},
body: JSON.stringify({
query: '你的查询内容'
})
onopen(response) {
if (response.ok) {
console.log('SSE连接成功');
} else {
throw new Error(`连接失败: ${response.status}`);
}
},
onmessage(event) {
console.log('收到消息:', event.data); //这里的消息就是流式输出的
},
onclose() {
console.log('SSE连接关闭');
},
onerror(err) {
console.error('发生错误:', err);
throw err; // 抛出错误会停止重试
}
});
三、封装成hooks
javascript
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { ref, onUnmounted } from 'vue';
interface EventSourceOptions {
url: string;
method?: 'GET' | 'POST';
body?: any;
onMessage?: (data: string) => void;
onOpen?: (response: Response) => void;
onError?: (err: any) => void;
}
export function useEventSource() {
const error = ref<any>(null);
const isLoading = ref<boolean>(false);
let controller: AbortController | null = null;
const fetchStream = async (options: EventSourceOptions) => {
isLoading.value = true;
error.value = null;
controller = new AbortController();
try {
await fetchEventSource(options.url, {
method: options.method || 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': "text/event-stream",
},
body: options.body ? JSON.stringify(options.body) : undefined,
signal: controller.signal,
// 建立链接
onopen: async (response) => {
if (response.ok && response.status == 200) {
options.onOpen?.(response);
}
},
// 接收消息
onmessage: (event) => {
if (event.data) {
options.onMessage?.(JSON.parse(event.data));
}
},
onerror: (err) => {
options.onError?.(err);
// 必须抛出错误才会停止重试
throw err;
},
onclose: () => {
isLoading.value = false;
}
});
} catch (err) {
console.log("链接失败");
error.value = err;
isLoading.value = false;
}
};
// 主动中断链接
const disconnect= () => {
if (controller) {
controller.abort();
isLoading.value = false;
}
};
onUnmounted(() => {
disconnect();
});
return {
isLoading,
fetchStream,
disconnect,
onerror,
onclose,
};
}
四、使用hooks
javascript
<template>
<div>
<button @click="sendMessage" :disabled="isLoading">
{{ isLoading ? '发送中...' : '发送消息' }}
</button>
<div class="stream-content">
{{ data }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { useEventSource } from '@/composables/useEventSource';
const { isLoading, fetchStream } = useEventSource();
const data = ref('')
const sendMessage = async () => {
await fetchStream({
url: '/api/chat',
method: 'POST',
body: {
content: '你好,请帮我回答这个问题'
},
onMessage: (content) => {
console.log('收到数据:', content);
data.value += content
},
onOpen: (response) => {
console.log('连接已建立');
},
onError: (err) => {
console.error('连接错误:', err);
}
});
};
</script>