EventSource 失败主因是服务端未遵循 SSE 协议:需返回 200 状态码、Content-Type: text/event-stream、Cache-Control: no-cache,每条消息以 data: ...\n\n 结尾;前端须用绝对 URL、正确配置 CORS 与 credentials,并区分 onmessage 和 addEventListener 的触发条件。

前端用 EventSource 接收 SSE 数据,90% 的失败不是因为写法错,而是服务端没按协议吐出合法流 —— 浏览器卡在 pending 状态、反复触发 error 事件、onmessage 完全不触发,基本都指向服务端响应头或数据格式违规。
EventSource 初始化必须带完整 URL,且不能跨域裸用
直接传相对路径(如 '/events')看似简洁,但一旦页面部署在子路径(比如 https://a.com/app/),实际请求会变成 https://a.com/app/events,而服务端路由可能只监听根路径。更关键的是跨域:若服务端没返回 Access-Control-Allow-Origin,连接会静默失败(onerror 触发但 event.target.readyState === 0),连 CORS 错误都看不到。
- 始终用绝对 URL,例如
new EventSource('https://api.example.com/sse') - 服务端必须返回
Access-Control-Allow-Origin: https://your-frontend-domain.com(不能是*,除非不带 credentials) - 如果需要携带 cookie 或认证头,前端要加
{ withCredentials: true },服务端还得加Access-Control-Allow-Credentials: true
onmessage 和 addEventListener 的触发条件完全不同
onmessage 只响应服务端未声明 event: 字段的“默认事件”;而自定义事件(如 event: update)必须用 addEventListener('update', ...) 显式监听,否则数据直接丢弃。
- 服务端输出
data: {"msg":"hello"}\n\n→ 触发onmessage - 服务端输出
event: update\ndata: {"status":"ok"}\n\n→ 只触发addEventListener('update', ...),onmessage不动 - 所有事件里的
event.data都是字符串,需手动JSON.parse(),别假设它自动转对象
错误处理不能只靠 onerror,要结合 readyState 判断真实状态
onerror 是个“垃圾桶事件”,网络断开、CORS 拒绝、服务端返回 404、甚至服务端写了 res.end() 提前关闭连接,都会进这里。仅靠 console.error(error) 根本看不出问题在哪。
- 检查
eventSource.readyState:0表示未连接(重连中),1表示已连接,2表示已关闭 - 如果
readyState === 0且频繁触发onerror,优先查服务端是否返回了 200 +text/event-stream+no-cache - 不要在
onerror里无条件new EventSource(...),避免雪崩重连;加退避逻辑,比如setTimeout(() => new EventSource(...), 5000)
服务端流式输出必须满足四要素,缺一不可
浏览器等待的是符合 SSE 协议的 HTTP 流,不是“能打印就行”的普通响应。Node.js/Python/PHP 都容易在以下环节翻车:
- 响应码必须是
200(304、204、500全部拒收) - 必须设置
Content-Type: text/event-stream,漏掉-stream后缀(如写成text/event)就失效 - 必须设
Cache-Control: no-cache,Safari 还要求must-revalidate才肯重连 - 每条消息结尾必须是双换行符
\n\n,单个\n或没换行,整个流解析失败
最简验证方式:用 curl -N http://localhost:3000/events 直接看原始响应流,确认开头有正确 header,后续每条数据以 data: ...\n\n 形式持续输出。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











