微信小程序SSE替代方案实战

微信小程序虽然不支持sse,但可通过分块传输技术模拟实现单向实时通讯功能。‌‌

技术实现现状与替代方案

1. 原生支持情况

微信小程序框架未内置SSE协议的EventSource API,官方文档中未包含相关接口说明。‌‌

2. 替代实现方案

  1. WebSocket方案 ‌。

    使用WebSocket作为官方支持的双向通信协议,适用于需要高实时性的场景(如聊天室、即时通知),API文档见:wx.connectSocket‌‌

  2. 分块传输模拟SSE ‌。

    通过wx.requestenableChunked参数启用分块传输,结合onChunkReceived事件监听数据流。需手动处理以下环节:

    • 二进制数据解析(如ArrayBuffer转字符串)。

    • 数据包完整性校验。

    • 事件流格式解析(按data:字段分割)。‌‌

具体实现步骤我这里使用的是uniapp,如果微信小程序原生的话只需要替换对应的api即可

javascript 复制代码
// src/hooks/useStreamingRequest.js
import {
	ref
} from 'vue'
import {
	BASE_URL
} from '@/config/index'
import {
	getStorageSync
} from '@/utils/storage'

export function useStreamingRequest() {
	const isLoading = ref(false)
	let requestTask = null

	/**
	 * 发起流式请求
	 * @param {string} url - 基础 URL
	 * @param {Object} data - 请求参数
	 * @param {Function} callback - 接收每一块数据的回调函数
	 */
	function sendStreamMessage(url, data, callback) {
		isLoading.value = true

		requestTask = uni.request({
			url: BASE_URL + url,
			method: 'POST',
			data,
			responseType: 'arraybuffer',
			enableChunked: true,
			header: {
				'content-type': 'application/json',
				'Authorization': getStorageSync('token')
			},
			success: (res) => {
				console.log('请求成功:', res)
			},
			fail: (error) => {
				console.error('请求失败:', error)
			},
			complete: () => {
				isLoading.value = false
			}
		})

		if (requestTask && typeof requestTask.onChunkReceived === 'function') {
			requestTask.onChunkReceived(res => {
				try {
					const textData = extractText(res.data)
					const parsedDataArray = extractAndParse(textData)

					if (parsedDataArray.length > 0 && callback) {
						parsedDataArray.forEach(item => callback(item))
					}
				} catch (e) {
					console.warn('分块数据处理出错', e)
				}
			})
		} else {
			console.warn('当前环境不支持 onChunkReceived')
		}
	}

	/**
	 * 中断请求
	 */
		function abortRequest() {
			if (requestTask) {
				requestTask.abort()
				requestTask = null
			}
		}

		return {
			sendStreamMessage,
			abortRequest,
			isLoading
		}
}

// -------------------------
// 工具函数提取复用
// -------------------------

/* const decoder = new TextDecoder('utf-8') */

/**
 * 将 ArrayBuffer 解码为 UTF-8 字符串
 */
function extractText(data) {
	try {
		if (typeof TextDecoder !== 'undefined') {
			return decoder.decode(new Uint8Array(data));
		} else {
			return decodeURIComponent(escape(String.fromCharCode.apply(null, new Uint8Array(data))));
		}
	} catch (error) {
		return decodeURIComponent(escape(String.fromCharCode.apply(null, new Uint8Array(data))));
	}
}

/**
 * 提取并解析 JSON 数据
 */
function extractAndParse(text) {
	const jsonRegex = /\{[^{}]*\}/gs
	const jsonMatches = text.match(jsonRegex) || []

	const results = []
	for (const match of jsonMatches) {
		try {
			results.push(JSON.parse(match))
		} catch (e) {
			console.error('JSON 解析失败:', match)
		}
	}
	return results
}

我这里将支持流式输出的方法封装为了一个工具函数方便在组件中调用

这段代码实现了一个用于处理流式请求的 Vue 组合式函数 useStreamingRequest,它允许逐步接收和处理服务器返回的数据块。下面是关键部分的解释:

  1. 核心功能

    • 使用 ref 创建响应式状态 isLoading 来跟踪请求状态

    • 通过 uni.request 发起支持分块传输的 HTTP 请求

    • 利用 onChunkReceived 监听并处理每个数据块

  2. 主要函数

    • sendStreamMessage: 发起流式请求,接收 URL、参数和回调函数

    • abortRequest: 中断当前正在进行的请求

    • 返回包含上述函数和加载状态的对象

  3. 数据处理流程

    • 使用 extractText 将二进制数据解码为字符串

    • 通过 extractAndParse 提取并解析其中的 JSON 数据

    • 支持兼容不同环境的文本解码方式

  4. 注意事项

    • 需要确保全局变量 decoder 被正确初始化(当前被注释掉了)

    • 回调函数会依次处理每个解析出的 JSON 对象

    • 包含错误处理机制以避免单个数据块解析失败影响整体流程

    • uni.request中的这个参数responseType: 'arraybuffer' 表示将响应数据作为二进制数据处理接收到的数据是原始的二进制格式(ArrayBuffer)适用于处理非文本数据,如文件下载、流式数据等需要手动解码为可读文本

    • uni.request中的这个参数enableChunked: true,true 表示启用分块接收功能允许逐步接收服务器返回的数据块不需要等待整个响应完成就可以处理部分数据

组件中调用

javascript 复制代码
import {
		useStreamingRequest
	} from '@/utils/useStreamingRequest'
const {
		sendStreamMessage
	} = useStreamingRequest()
const requestParameters = ref({})
	const aiContent = ref('') //内容
	const displayContent = ref('') // 展示内容,不含第一个字符
	const status = ref('') //ai生成状态
	const aiMessage = ref('') //ai生成内容
	const shopId = ref('') //商户id
	const taskId = ref('')
	const dataShows = ref(false) //ai生成内容显示隐藏
	const complete = ref(false) //ai生成内容完成
/* 发送ai */
	const startStream = () => {
		const url = '/miniProgram/sendOnSitePhoto'
		const data = {
			photoUrl: requestParameters.value.photoUrl,
			onSiteTaskPositionDetailId: requestParameters.value.onSiteTaskPositionDetailId,
			inspectionName: requestParameters.value.checkItemsName
		}

		/* const data = {
			photoUrl: "https://gjian-bucket.oss-cn-beijing.aliyuncs.com/zjj/min/title.png",
			onSiteTaskPositionDetailId: '1926265598757675010'
		} */
		sendStreamMessage(url, data, handleAIResult)
	}

	//接收流式数据回调函数
	const handleAIResult = async (event) => {
		
		if (event.event === 'message_end') {
			console.log("生成完成")
			aiMessage.value = aiContent.value
			console.log(aiMessage.value)
			if (event.answer) {
				aiContent.value += event.answer
			}
			if (status.value == 1) { //ai解析结果为异常时
				complete.value = true
			}

		} else {
			if (event.answer) {
				dataShows.value = true
				aiContent.value += event.answer;
			}
		}
		status.value = aiContent.value[0]
		// 设置展示内容(去除第一个字符)
		displayContent.value = aiContent.value.slice(1)
		if (status.value == 2) {
			const confirm = await showModal('拍照数据有误,请从新上传!')
			if (confirm) {
				useRouter("/subpkg/take_pictures/take_pictures")
			} else {
				console.log('用户点击了取消')
			}
		}
	}

内容展示

html 复制代码
<view class="card-content" v-if="dataShows">
						<rich-text :nodes="displayContent.replace(/\n/g, '<br />').replace(/[#*]/g, '')"
							style="font-weight: 400;font-size: 28rpx;color: #000000;line-height: 40rpx;"></rich-text>
					</view>
相关推荐
vayy3 小时前
uniapp中 ios端 scroll-view 组件内部子元素z-index失效问题
前端·ios·微信小程序·uni-app
阿彬学java4 小时前
Charles抓包微信小程序请求响应数据
微信小程序·小程序
傻傻有内涵的我4 小时前
【微信小程序】分别解决H5的跨域代理问题 和小程序正常不需要代理问题
微信小程序·小程序
毕设源码-钟学长16 小时前
【开题答辩全过程】以 微信小程序的医院挂号预约系统为例,包含答辩的问题和答案
微信小程序·小程序
bmy-happy1 天前
实验2 天气预报
微信小程序·小程序
兰亭妙微1 天前
从线到机:AI 与多模态交互如何重塑 B 端与 App 界面设计
人工智能·小程序·交互·用户体验设计公司
青青子衿越1 天前
微信小程序web-view嵌套H5,小程序与H5通信
前端·微信小程序·小程序
乔公子搬砖1 天前
小程序开发提效:npm支持、Vant Weapp组件库与API Promise化(八)
前端·javascript·微信小程序·js·promise·vagrant·事件绑定
!win !2 天前
uni-app支付宝端彻底禁掉下拉刷新效果
前端·小程序·uni-app