更新记录
1.1.0(2026-08-05)
✨v1.0.0 首次发布
平台兼容性
uni-app(5.08)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | × | √ | √ | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-sse
uni-sse 是一个面向 uni-app App 端的原生 UTS SSE(Server-Sent Events,服务端推送事件)客户端。插件在 Android 端基于 HttpURLConnection,在 iOS 端基于 URLSessionDataDelegate 实现,适合消息通知、状态推送、日志流、AI 对话等需要服务端持续返回文本数据的场景。
插件同时支持:
- 标准 SSE 协议解析,包括
event、data、id、retry和注释帧。 - GET 长连接和带请求体的 POST 流式请求。
- 自动重连、指数退避和
Last-Event-ID断点续传。 - 标准 SSE 与普通分片文本两种解析模式。
- 同时管理多个连接,也可以主动关闭单个或全部连接。
平台兼容性
| 平台 | 支持情况 | 最低要求 |
|---|---|---|
| App Android(app-vue、app-uvue) | 支持 | Android 5.0,API 21 |
| App iOS(app-vue、app-uvue) | 支持 | iOS 12.0 |
| App nvue | 未验证 | package.json 中标记为 u |
| H5 | 不支持 | 调用时返回“不支持当前平台”错误 |
| 微信/支付宝等小程序 | 不支持 | 调用时返回“不支持当前平台”错误 |
请使用 HBuilderX 4.27 或更高版本。插件的持续回调依赖 @UTSJS.keepAlive,低版本 HBuilderX 可能无法正常保留回调。
安装与引入
将插件导入项目后,确认目录结构如下:
uni_modules/
└─ uni-sse/
├─ package.json
├─ readme.md
└─ utssdk/
如果项目配置了 src 作为源码目录,则插件路径通常是 src/uni_modules/uni-sse。
在页面、组件或业务模块中直接引入需要的 API:
import {
closeAllSse,
closeSse,
connectSse,
isSseConnected
} from '@/uni_modules/uni-sse'
UTS 原生代码会在 App 运行或发行时参与编译。修改插件原生代码后,如果项目使用自定义基座,需要重新制作基座再测试。
快速开始
下面示例创建一个标准的 GET SSE 连接:
import { closeSse, connectSse } from '@/uni_modules/uni-sse'
const connectionId = connectSse(
{
url: 'https://example.com/events',
method: 'GET',
headers: {
Authorization: 'Bearer your-token'
},
query: {
clientId: '10001'
}
},
{
onStart() {
console.log('SSE 请求开始')
},
onOpen(event) {
console.log('SSE 已连接', event.statusCode, event.headers)
},
onMessage(value, event) {
console.log('事件名称:', event.event)
console.log('消息 ID:', event.id)
console.log('消息内容:', value)
},
onRetry(event) {
console.log(`第 ${event.retryCount} 次重连将在 ${event.delay}ms 后开始`)
},
onError(error) {
console.error('SSE 请求异常', error.errCode, error.errMsg)
},
onComplete(event) {
console.log('SSE 流正常结束', event.reason)
},
onClose(event) {
console.log('SSE 连接已关闭', event.reason)
}
}
)
// 页面退出、业务结束或用户主动停止时关闭连接。
function stopSse(): void {
closeSse(connectionId)
}
connectSse 会立即返回连接 ID,真正建连成功后才会触发 onOpen。
回调必须作为
connectSse的第二个参数传入,不要把onMessage、onError等回调写进第一个配置对象。UTS 可能会在首次调用结束后释放错误位置上的回调,导致后续收不到流式事件。
API 概览
| API | 说明 | 返回值 |
|---|---|---|
connectSse(config, callbacks?) |
创建 SSE 连接并注册生命周期回调 | 当前连接 ID |
closeSse(connectionId) |
主动关闭指定连接 | 找到运行中的连接并发起关闭时返回 true |
closeAllSse() |
主动关闭插件创建的全部连接 | 无 |
isSseConnected(connectionId) |
判断连接是否仍在运行或等待重连 | boolean |
connectSse
function connectSse(
config: SseConnectConfig,
callbacks?: SseConnectCallbacks
): string
连接在原生线程中异步运行,函数本身不会等待服务器响应。如果传入的 id 与现有连接重复,插件会先关闭旧连接,再使用该 ID 创建新连接。
SseConnectConfig
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
id |
string |
否 | 自动生成 | 业务指定的稳定连接 ID。建议仅在需要按业务标识管理连接时传入 |
url |
string |
是 | 无 | SSE 服务完整地址 |
method |
string |
否 | 见说明 | 未传 body 时默认 GET,传入 body 时默认 POST,也可以显式指定 |
headers |
UTSJSONObject |
否 | {} |
自定义请求头。原生端会把每个值转成字符串 |
query |
UTSJSONObject |
否 | {} |
追加到 URL 的查询参数,参数名称和值会进行 URL 编码 |
body |
string |
否 | 无 | UTF-8 请求体,常用于 POST AI 流式接口 |
contentType |
string |
否 | application/json; charset=utf-8 |
传入 body 且 headers 未设置 Content-Type 时使用 |
connectTimeout |
number |
否 | 15000 |
建连超时时间,单位毫秒;当前主要用于 Android 原生请求 |
readTimeout |
number |
否 | 0 |
读取超时时间,单位毫秒;0 表示按长连接处理,不主动设置短读取超时 |
reconnect |
boolean |
否 | true |
连接异常或服务端断开后是否自动重连 |
reconnectDelay |
number |
否 | 3000 |
第一次重连前等待时间,单位毫秒 |
maxReconnectDelay |
number |
否 | 30000 |
指数退避后的最大重连间隔,单位毫秒 |
maxRetries |
number |
否 | -1 |
最大连续重试次数,-1 表示不限制;成功建立 2xx 连接后计数会重置 |
lastEventId |
string |
否 | '' |
首次请求携带的 Last-Event-ID,用于从指定事件继续接收 |
doneWhenData |
string |
否 | '' |
当消息 data 与此值完全相等时,将流视为正常完成并关闭 |
parseMode |
'sse' \| 'raw' |
否 | 'sse' |
sse 解析标准事件帧;raw 按原生网络分片返回普通文本 |
debug |
boolean |
否 | false |
开启后,在达到最大重试次数时额外通过 onError 返回诊断信息 |
插件默认发送以下请求头,自定义 headers 可以覆盖同名请求头:
Accept: text/event-stream
Cache-Control: no-cache
Accept-Encoding: identity
Android 端还会默认发送 Connection: keep-alive。需要传递登录态时,可以通过 headers.Authorization 或项目约定的其他请求头传入。
建议只在 headers 和 query 中传递字符串、数字、布尔值等简单值,并避免传入 null、数组或嵌套对象,以确保 Android 与 iOS 的字符串转换结果一致。
SseConnectCallbacks
| 回调 | 参数 | 触发时机 |
|---|---|---|
onStart |
无 | 配置校验通过,原生请求准备发起时触发一次 |
onOpen |
SseOpenEvent |
收到 2xx HTTP 响应、流成功打开时触发;重连成功后会再次触发 |
onChunk |
SseChunkEvent |
每次收到原生响应分片时触发,不等待完整 SSE 消息 |
onMessage |
value, SseMessageEvent |
sse 模式收到完整事件帧,或 raw 模式收到一个原生分片时触发 |
onComment |
SseCommentEvent |
收到以 : 开头的 SSE 注释帧时触发,常用于处理服务端心跳 |
onRetry |
SseRetryEvent |
已决定自动重连、开始等待重连间隔时触发 |
onError |
SseErrorEvent |
配置、网络、HTTP 状态或数据解析异常时触发 |
onComplete |
SseCloseEvent |
因 doneWhenData 命中或正常 EOF 而结束时触发 |
onClose |
SseCloseEvent |
连接最终结束时触发一次,包括正常结束、异常结束和主动关闭 |
onError 不一定表示连接已经结束。如果 error.canRetry 和 reconnect 都允许重连,后面还会触发 onRetry;只有 onClose 表示该连接已经最终结束。
正常完成时,插件会先触发 onComplete,随后再触发 onClose。建议在 onComplete 中处理“业务完成”,在 onClose 中只清理连接状态,避免同一业务逻辑执行两次。
回调事件字段
SseOpenEvent
| 字段 | 类型 | 说明 |
|---|---|---|
connectionId |
string |
当前连接 ID |
statusCode |
number |
HTTP 响应状态码 |
headers |
UTSJSONObject |
服务端响应头,不同平台返回的响应头名称大小写可能不同 |
SseMessageEvent
| 字段 | 类型 | 说明 |
|---|---|---|
connectionId |
string |
当前连接 ID |
event |
string |
event: 声明的事件名称;未声明时为 message,raw 模式为 chunk |
data |
string |
当前消息内容,与 onMessage 的第一个参数一致 |
id |
string |
当前已记录的最后消息 ID,没有时为空字符串 |
retry |
number |
当前帧声明的 retry: 毫秒数,未声明时为 -1 |
raw |
string |
当前消息解析前的原始文本,不包含用于分隔事件的空行 |
标准 SSE 帧包含多行 data: 时,插件会使用换行符 \n 将各行内容合并后再触发 onMessage。
SseChunkEvent
| 字段 | 类型 | 说明 |
|---|---|---|
connectionId |
string |
当前连接 ID |
data |
string |
本次收到的原始 UTF-8 文本分片 |
网络分片边界不等于消息边界。一次 onChunk 可能只包含半个 SSE 帧,也可能同时包含多个 SSE 帧,因此标准 SSE 业务应优先使用 onMessage。
SseCommentEvent
| 字段 | 类型 | 说明 |
|---|---|---|
connectionId |
string |
当前连接 ID |
comment |
string |
去掉行首 : 和前导空格后的注释内容 |
raw |
string |
服务端发送的原始注释行 |
SseRetryEvent
| 字段 | 类型 | 说明 |
|---|---|---|
connectionId |
string |
当前连接 ID |
retryCount |
number |
当前连续重试次数,从 1 开始 |
delay |
number |
本次重连前需要等待的毫秒数 |
lastEventId |
string |
下次请求会携带的最后消息 ID |
reason |
string |
触发重连的原因,例如 eof、error、timeout 或 http_status |
SseErrorEvent
| 字段 | 类型 | 说明 |
|---|---|---|
connectionId |
string |
当前连接 ID |
errCode |
number |
插件错误码 |
errMsg |
string |
错误说明 |
statusCode |
number |
HTTP 状态码;尚未收到响应时通常为 0 |
canRetry |
boolean |
此类错误是否允许重试,不代表一定会重试 |
cause |
string |
原生异常详情或非 2xx HTTP 响应正文,可用于日志和排错 |
SseCloseEvent
| 字段 | 类型 | 说明 |
|---|---|---|
connectionId |
string |
当前连接 ID |
reason |
string |
最终关闭原因 |
statusCode |
number |
关闭前最近一次 HTTP 状态码 |
lastEventId |
string |
关闭前记录的最后消息 ID |
closedByUser |
boolean |
是否由 closeSse、closeAllSse 或同 ID 新连接主动关闭 |
常见关闭原因如下:
reason |
说明 |
|---|---|
manual |
业务主动关闭连接 |
done |
收到与 doneWhenData 完全匹配的数据 |
eof |
服务端正常结束响应流,且插件不再重连 |
error |
网络或请求异常,且插件不再重连 |
timeout |
Android 端读取超时,且插件不再重连 |
http_status |
iOS 端收到非 2xx 状态,且插件不再重连 |
invalid_url |
iOS 端无法解析请求 URL |
cancelled |
iOS 原生请求被系统取消 |
unsupported |
在非 App Android/iOS 平台调用插件 |
部分异常在 Android 与 iOS 上可能使用不同的 reason。跨平台业务判断应优先区分 manual、done、eof 与“其他异常”,详细错误信息从 onError 获取。
标准 SSE 模式
parseMode: 'sse' 是默认模式。服务端需要按照 SSE 协议返回数据,并使用空行结束每个事件:
: heartbeat
id: 1001
event: notice
retry: 5000
data: 第一行内容
data: 第二行内容
上面的消息会触发一次 onMessage:
onMessage(value, event) {
console.log(event.event) // notice
console.log(event.id) // 1001
console.log(event.retry) // 5000
console.log(value) // 第一行内容\n第二行内容
}
解析规则:
event:设置事件名称,省略时使用message。- 多个
data:行使用\n合并。 id:会更新连接保存的最后消息 ID,重连时自动通过Last-Event-ID请求头发送。- 非负整数
retry:会更新后续重连的基础等待时间。 - 以
:开头的行属于注释帧,通过onComment返回,不会触发onMessage。 - 未识别字段会被忽略。
- 空行表示一个事件结束。服务端如果不发送空行,客户端通常要等到连接结束后才能派发最后一条消息。
raw 分片模式
如果服务端返回的是普通 chunked 文本,而不是 data: ...\n\n 格式的标准 SSE,可以使用 parseMode: 'raw':
const connectionId = connectSse(
{
url: 'https://example.com/text-stream',
method: 'POST',
headers: {
Authorization: 'Bearer your-token'
},
body: JSON.stringify({ question: '你好' }),
parseMode: 'raw',
reconnect: false
},
{
onMessage(chunk, event) {
console.log(event.event) // chunk
console.log(chunk)
},
onComplete() {
console.log('文本流接收完成')
},
onError(error) {
console.error(error.errMsg)
}
}
)
raw 模式中的一次回调只代表一次底层网络分片,不保证对应一个完整 JSON、一行文本或一个完整字符流业务单元。业务侧如果需要按行或按 JSON 解析,应先累计字符串,再自行切分完整数据。
在 raw 模式下使用 doneWhenData 时,结束标记必须完整出现在单个分片中,插件会将该分片去掉首尾空白后再比较。如果结束标记可能被拆到两个分片中,建议由业务侧累计判断并调用 closeSse。
AI POST 流式请求示例
很多 AI 接口虽然使用 POST 请求,但响应仍然是标准 SSE,例如最终发送 data: [DONE]。这类一次性请求应关闭自动重连,防止流结束后再次提交相同问题:
import { closeSse, connectSse } from '@/uni_modules/uni-sse'
let aiConnectionId = ''
function askAi(question: string): void {
if (aiConnectionId) {
closeSse(aiConnectionId)
}
aiConnectionId = connectSse(
{
url: 'https://example.com/v1/chat/stream',
method: 'POST',
headers: {
Authorization: 'Bearer your-token'
},
body: JSON.stringify({ question }),
contentType: 'application/json; charset=utf-8',
parseMode: 'sse',
reconnect: false,
doneWhenData: '[DONE]'
},
{
onMessage(value) {
if (value === '[DONE]') {
return
}
// 按服务端协议解析每一段 data 内容。
console.log('AI 流式数据:', value)
},
onError(error) {
console.error('AI 请求失败:', error.errMsg, error.cause)
},
onComplete() {
console.log('AI 回复完成')
},
onClose(event) {
// 避免旧请求的延迟回调清空刚创建的新连接 ID。
if (aiConnectionId === event.connectionId) {
aiConnectionId = ''
}
}
}
)
}
function stopAi(): void {
if (!aiConnectionId) {
return
}
closeSse(aiConnectionId)
aiConnectionId = ''
}
doneWhenData 比较的是解析后的完整 data,不会把结束标记当成正则表达式,也不会自动解析 JSON。如果服务端发送 data: {"done":true},应传入完全相同的字符串,或者在 onMessage 中解析 JSON 后主动关闭。
自动重连机制
reconnect 默认是 true,适合需要持续在线的消息推送连接。重连流程如下:
- 连接因可重试网络错误、读取超时、可重试 HTTP 状态或 EOF 中断。
- 先触发
onError(如果本次中断属于错误)。 - 插件判断
reconnect、canRetry和maxRetries。 - 允许重连时触发
onRetry,等待delay毫秒后重新请求。 - 重连请求自动携带最近一次收到的
Last-Event-ID。 - 重连成功后再次触发
onOpen,连续重试计数与退避时间恢复初始值。
默认重连间隔从 3 秒开始,之后按 3 秒、6 秒、12 秒、24 秒、30 秒的方式增长,并受 maxReconnectDelay 限制。如果服务端返回合法的 retry: 字段,插件会用该值更新重连基础间隔。
下列 HTTP 状态允许自动重试:
408、409、425、429、所有大于等于 500 的状态码
其他 4xx 状态通常属于认证、权限或参数问题,插件不会自动重试。
对于 AI 问答、文本生成等一次性 POST 流,请显式设置 reconnect: false。否则服务端正常关闭响应后,默认重连可能重复提交请求。
连接管理与页面生命周期
关闭一个连接
const closed = closeSse(connectionId)
if (!closed) {
console.log('连接不存在或已经结束')
}
关闭全部连接
closeAllSse()
判断连接状态
if (isSseConnected(connectionId)) {
console.log('连接仍在运行或等待重连')
}
页面卸载、用户退出登录、切换账号或业务不再需要推送时,应主动关闭连接,避免后台继续接收数据:
import { onUnmounted } from 'vue'
import { closeSse } from '@/uni_modules/uni-sse'
onUnmounted(() => {
if (connectionId) {
closeSse(connectionId)
}
})
如果一个页面可能同时创建多个连接,建议把连接 ID 保存到数组或 Set 中,在页面卸载时逐个关闭,或者在确认不会影响其他页面连接的情况下使用 closeAllSse()。
错误码
| 错误码 | 常量 | 说明 |
|---|---|---|
9011001 |
UNI_SSE_ERROR_INVALID_OPTIONS |
配置不合法,例如 URL 为空或无法解析 |
9011002 |
UNI_SSE_ERROR_UNSUPPORTED |
当前平台不是 App Android 或 App iOS |
9011003 |
UNI_SSE_ERROR_REQUEST_FAILED |
网络请求失败、读取超时或达到最大重试次数 |
9011004 |
UNI_SSE_ERROR_HTTP_STATUS |
服务端返回非 2xx HTTP 状态码 |
9011005 |
UNI_SSE_ERROR_PARSE_FAILED |
UTS 层无法解析原生回调数据 |
非 2xx 响应的正文会尽量放在 SseErrorEvent.cause 中,便于查看服务端返回的业务错误。请勿把可能包含令牌、用户隐私或完整响应内容的 cause 直接展示给最终用户或上传到不安全的日志系统。
服务端要求
标准 SSE 接口建议至少返回以下响应头:
Content-Type: text/event-stream; charset=utf-8
Cache-Control: no-cache
Connection: keep-alive
并确保:
- 每个事件以空行结束。
- 数据以 UTF-8 编码返回。
- 每发送一条事件就及时刷新响应缓冲区。
- 长时间没有业务消息时定期发送
: heartbeat\n\n注释帧维持连接。 - 需要断点续传时返回递增或唯一的
id:,并在重连请求中读取Last-Event-ID。 - 经过 Nginx 等反向代理时关闭响应缓冲,例如按实际部署配置
proxy_buffering off,否则客户端可能长时间收不到分片。
原生 App 请求不受浏览器 CORS 机制限制,但服务端仍需要正确处理鉴权、HTTPS 证书、网关超时和反向代理缓冲。
常见问题
onChunk 有数据,但 onMessage 没有触发
通常是服务端没有按标准 SSE 格式返回,或每条消息结尾缺少空行。请检查响应是否类似 data: 内容\n\n。如果服务端只能返回普通分片文本,改用 parseMode: 'raw'。
流结束后请求又自动发起了一次
reconnect 默认是 true,服务端关闭流会被视为可重连 EOF。一次性流式请求请设置 reconnect: false,或者配置可靠的 doneWhenData 结束标记。
已收到 [DONE],连接却没有结束
标准 SSE 响应应为 data: [DONE]\n\n,并配置 doneWhenData: '[DONE]'。该比较区分大小写且要求完整相等。如果 [DONE] 被包在 JSON 中,请在 onMessage 中解析后主动调用 closeSse。
为什么 onError 后又收到了 onRetry 和 onOpen
这是自动重连的正常生命周期。onError 表示本次请求发生错误,onRetry 表示插件决定重试,onOpen 表示新请求已经成功打开。最终是否结束以 onClose 为准。
为什么正常完成后 onComplete 和 onClose 都触发了
onComplete 用于表达流正常完成,onClose 用于统一通知连接资源已经关闭。正常完成时两者会依次触发,异常或主动关闭通常只触发 onClose。
为什么在 H5 或小程序中无法使用
该插件依赖 Android/iOS 原生流式网络能力,仅支持 App。H5 可以使用浏览器 EventSource 或 fetch 流,小程序需要使用对应平台提供的流式请求能力。
安全与发布说明
- Android 插件清单已声明
INTERNET和ACCESS_NETWORK_STATE权限。 - 当前 Android 配置允许明文 HTTP,iOS ATS 配置也允许任意网络加载,主要用于开发环境调试。
- 生产环境建议使用 HTTPS,并根据实际域名收紧 Android
usesCleartextTraffic与 iOS ATS 配置。 - 不要在
debug日志、错误回调或页面提示中泄露 Authorization、Cookie、用户隐私和完整服务端响应。 - 长连接会持续占用网络和系统资源,应在页面卸载、退出登录或应用业务结束时及时关闭。

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 0
赞赏 0
下载 12490354
赞赏 1939
赞赏
京公网安备:11010802035340号