更新记录

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 协议解析,包括 eventdataidretry 和注释帧。
  • 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 的第二个参数传入,不要把 onMessageonError 等回调写进第一个配置对象。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 传入 bodyheaders 未设置 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 或项目约定的其他请求头传入。

建议只在 headersquery 中传递字符串、数字、布尔值等简单值,并避免传入 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.canRetryreconnect 都允许重连,后面还会触发 onRetry;只有 onClose 表示该连接已经最终结束。

正常完成时,插件会先触发 onComplete,随后再触发 onClose。建议在 onComplete 中处理“业务完成”,在 onClose 中只清理连接状态,避免同一业务逻辑执行两次。

回调事件字段

SseOpenEvent

字段 类型 说明
connectionId string 当前连接 ID
statusCode number HTTP 响应状态码
headers UTSJSONObject 服务端响应头,不同平台返回的响应头名称大小写可能不同

SseMessageEvent

字段 类型 说明
connectionId string 当前连接 ID
event string event: 声明的事件名称;未声明时为 messageraw 模式为 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 触发重连的原因,例如 eoferrortimeouthttp_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 是否由 closeSsecloseAllSse 或同 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。跨平台业务判断应优先区分 manualdoneeof 与“其他异常”,详细错误信息从 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,适合需要持续在线的消息推送连接。重连流程如下:

  1. 连接因可重试网络错误、读取超时、可重试 HTTP 状态或 EOF 中断。
  2. 先触发 onError(如果本次中断属于错误)。
  3. 插件判断 reconnectcanRetrymaxRetries
  4. 允许重连时触发 onRetry,等待 delay 毫秒后重新请求。
  5. 重连请求自动携带最近一次收到的 Last-Event-ID
  6. 重连成功后再次触发 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 后又收到了 onRetryonOpen

这是自动重连的正常生命周期。onError 表示本次请求发生错误,onRetry 表示插件决定重试,onOpen 表示新请求已经成功打开。最终是否结束以 onClose 为准。

为什么正常完成后 onCompleteonClose 都触发了

onComplete 用于表达流正常完成,onClose 用于统一通知连接资源已经关闭。正常完成时两者会依次触发,异常或主动关闭通常只触发 onClose

为什么在 H5 或小程序中无法使用

该插件依赖 Android/iOS 原生流式网络能力,仅支持 App。H5 可以使用浏览器 EventSourcefetch 流,小程序需要使用对应平台提供的流式请求能力。

安全与发布说明

  • Android 插件清单已声明 INTERNETACCESS_NETWORK_STATE 权限。
  • 当前 Android 配置允许明文 HTTP,iOS ATS 配置也允许任意网络加载,主要用于开发环境调试。
  • 生产环境建议使用 HTTPS,并根据实际域名收紧 Android usesCleartextTraffic 与 iOS ATS 配置。
  • 不要在 debug 日志、错误回调或页面提示中泄露 Authorization、Cookie、用户隐私和完整服务端响应。
  • 长连接会持续占用网络和系统资源,应在页面卸载、退出登录或应用业务结束时及时关闭。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

Android:android.permission.INTERNET

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。