uni-app WebSocket:实时通信的跨端解决方案
【免费下载链接】uni-app A cross-platform framework using Vue.js 项目地址: https://gitcode.com/dcloud/uni-app
引言:实时通信的跨端挑战
在移动应用开发中,实时通信功能已成为现代应用的标配。无论是即时聊天、实时数据推送、在线协作还是游戏对战,都需要稳定可靠的实时通信能力。然而,在多端开发场景下,实现一套代码在不同平台(微信小程序、支付宝小程序、H5、App等)上都能正常工作的WebSocket通信,往往面临诸多挑战:
- 平台差异:各平台WebSocket API存在细微差异
- 连接管理:不同平台的连接生命周期管理不一致
- 数据格式:二进制数据传输在各平台支持度不同
- 网络环境:移动端网络不稳定带来的重连机制需求
uni-app作为跨端开发框架,提供了统一的WebSocket API,让开发者能够用一套代码实现全平台的实时通信功能。
uni-app WebSocket API 核心功能
基础连接管理
uni-app提供了完整的WebSocket生命周期管理API:
// 建立WebSocket连接
const socketTask = uni.connectSocket({
url: 'wss://example.com/websocket',
header: {
'X-Custom-Header': 'value'
},
protocols: ['protocol1'],
success: (res) => {
console.log('连接成功', res)
},
fail: (err) => {
console.error('连接失败', err)
}
})
// 监听WebSocket连接打开事件
socketTask.onOpen((res) => {
console.log('WebSocket连接已打开')
// 发送消息
socketTask.send({
data: 'Hello Server',
success: () => {
console.log('消息发送成功')
}
})
})
// 监听收到服务器消息
socketTask.onMessage((res) => {
console.log('收到服务器消息:', res.data)
})
// 监听连接错误
socketTask.onError((err) => {
console.error('WebSocket连接错误:', err)
})
// 监听连接关闭
socketTask.onClose((res) => {
console.log('WebSocket连接已关闭', res)
})
// 主动关闭连接
socketTask.close({
code: 1000,
reason: '正常关闭',
success: () => {
console.log('连接已关闭')
}
})
多协议支持
uni-app WebSocket支持多种数据格式传输:
| 数据类型 | 支持平台 | 使用场景 |
|---|---|---|
| String | 所有平台 | 文本消息、JSON数据 |
| ArrayBuffer | 所有平台 | 二进制数据、文件传输 |
| Blob | H5平台 | 大文件传输 |
// 发送文本消息
socketTask.send({
data: JSON.stringify({ type: 'message', content: 'Hello' })
})
// 发送二进制数据
const buffer = new ArrayBuffer(16)
const view = new Uint8Array(buffer)
for (let i = 0; i < 16; i++) {
view[i] = i
}
socketTask.send({
data: buffer
})
高级特性与最佳实践
连接状态管理
class WebSocketManager {
constructor() {
this.socketTask = null
this.reconnectCount = 0
this.maxReconnect = 5
this.reconnectTimer = null
this.isConnected = false
}
connect(url, protocols = []) {
this.socketTask = uni.connectSocket({
url,
protocols,
success: () => {
this.setupEventListeners()
},
fail: (err) => {
console.error('连接失败', err)
this.handleReconnect()
}
})
}
setupEventListeners() {
this.socketTask.onOpen(() => {
this.isConnected = true
this.reconnectCount = 0
console.log('WebSocket连接成功')
})
this.socketTask.onError((err) => {
console.error('WebSocket错误', err)
this.isConnected = false
this.handleReconnect()
})
this.socketTask.onClose((res) => {
this.isConnected = false
console.log('WebSocket连接关闭', res)
if (res.code !== 1000) {
this.handleReconnect()
}
})
}
handleReconnect() {
if (this.reconnectCount >= this.maxReconnect) {
console.log('达到最大重连次数')
return
}
this.reconnectCount++
const delay = Math.min(1000 * Math.pow(2, this.reconnectCount), 30000)
console.log(`将在 ${delay}ms 后尝试第 ${this.reconnectCount} 次重连`)
this.reconnectTimer = setTimeout(() => {
this.connect(this.socketTask.url)
}, delay)
}
send(data) {
if (!this.isConnected) {
console.warn('WebSocket未连接,消息发送失败')
return Promise.reject(new Error('WebSocket未连接'))
}
return new Promise((resolve, reject) => {
this.socketTask.send({
data,
success: resolve,
fail: reject
})
})
}
close(code = 1000, reason = '正常关闭') {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
}
if (this.socketTask) {
this.socketTask.close({ code, reason })
}
}
}
心跳检测机制
class HeartbeatManager {
constructor(socketManager, interval = 30000) {
this.socketManager = socketManager
this.interval = interval
this.heartbeatTimer = null
this.lastPongTime = Date.now()
}
start() {
this.heartbeatTimer = setInterval(() => {
if (this.socketManager.isConnected) {
// 发送心跳包
this.socketManager.send(JSON.stringify({
type: 'heartbeat',
timestamp: Date.now()
})).catch(err => {
console.error('心跳发送失败', err)
})
}
}, this.interval)
// 监听服务器响应
this.socketManager.socketTask.onMessage((res) => {
try {
const message = JSON.parse(res.data)
if (message.type === 'pong') {
this.lastPongTime = Date.now()
}
} catch (e) {
// 非JSON消息,忽略
}
})
}
stop() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer)
}
}
checkHealth() {
const now = Date.now()
if (now - this.lastPongTime > this.interval * 2) {
console.warn('心跳检测失败,可能连接已断开')
this.socketManager.handleReconnect()
}
}
}
跨平台兼容性处理
条件编译处理平台差异
// 平台特定的WebSocket配置
const platformConfig = {
// #ifdef MP-WEIXIN
protocols: ['wxs'], // 微信小程序特定协议
// #endif
// #ifdef APP-PLUS
perMessageDeflate: false, // App端禁用压缩
// #endif
// #ifdef H5
withCredentials: true, // H5端携带cookie
// #endif
}
// 二进制数据处理兼容
function processBinaryData(data) {
// #ifdef MP-WEIXIN || MP-ALIPAY
// 小程序平台需要特殊处理
return data
// #endif
// #ifdef H5 || APP-PLUS
// H5和App平台标准处理
if (data instanceof ArrayBuffer) {
return new Uint8Array(data)
}
return data
// #endif
}
网络状态监听
// 监听网络状态变化
uni.onNetworkStatusChange((res) => {
console.log('网络状态变化:', res)
if (!res.isConnected) {
console.warn('网络连接断开')
// 暂停消息发送,等待网络恢复
} else {
console.log('网络已恢复')
// 尝试重新连接WebSocket
}
})
// 获取当前网络状态
uni.getNetworkType({
success: (res) => {
console.log('当前网络类型:', res.networkType)
}
})
实战案例:实时聊天应用
消息协议设计
// 消息类型定义
const MessageType = {
TEXT: 'text',
IMAGE: 'image',
FILE: 'file',
SYSTEM: 'system',
HEARTBEAT: 'heartbeat',
PONG: 'pong'
}
// 消息结构
class Message {
constructor(type, content, options = {}) {
this.id = options.id || this.generateId()
this.type = type
this.content = content
this.timestamp = options.timestamp || Date.now()
this.sender = options.sender
this.receiver = options.receiver
this.extra = options.extra || {}
}
generateId() {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
}
toJSON() {
return JSON.stringify({
id: this.id,
type: this.type,
content: this.content,
timestamp: this.timestamp,
sender: this.sender,
receiver: this.receiver,
extra: this.extra
})
}
static fromJSON(json) {
try {
const data = JSON.parse(json)
return new Message(
data.type,
data.content,
{
id: data.id,
timestamp: data.timestamp,
sender: data.sender,
receiver: data.receiver,
extra: data.extra
}
)
} catch (e) {
console.error('消息解析失败', e)
return null
}
}
}
聊天室实现
class ChatRoom {
constructor() {
this.wsManager = new WebSocketManager()
this.heartbeatManager = null
this.messageQueue = []
this.messageHandlers = new Map()
}
connect(roomId, userId) {
const url = `wss://chat.example.com/ws?room=${roomId}&user=${userId}`
this.wsManager.connect(url)
this.wsManager.socketTask.onOpen(() => {
console.log('已加入聊天室')
this.setupMessageHandlers()
this.startHeartbeat()
this.processMessageQueue()
})
this.wsManager.socketTask.onMessage((res) => {
this.handleIncomingMessage(res.data)
})
}
setupMessageHandlers() {
this.registerHandler(MessageType.TEXT, this.handleTextMessage.bind(this))
this.registerHandler(MessageType.IMAGE, this.handleImageMessage.bind(this))
this.registerHandler(MessageType.SYSTEM, this.handleSystemMessage.bind(this))
}
registerHandler(type, handler) {
this.messageHandlers.set(type, handler)
}
handleIncomingMessage(rawData) {
const message = Message.fromJSON(rawData)
if (!message) return
const handler = this.messageHandlers.get(message.type)
if (handler) {
handler(message)
}
}
handleTextMessage(message) {
console.log('收到文本消息:', message.content)
// 更新UI显示消息
uni.$emit('chat-message', message)
}
handleImageMessage(message) {
console.log('收到图片消息:', message.content)
// 处理图片消息
}
handleSystemMessage(message) {
console.log('系统消息:', message.content)
// 处理系统通知
}
sendMessage(type, content, options = {}) {
const message = new Message(type, content, options)
if (this.wsManager.isConnected) {
this.wsManager.send(message.toJSON())
} else {
this.messageQueue.push(message)
}
}
processMessageQueue() {
while (this.messageQueue.length > 0 && this.wsManager.isConnected) {
const message = this.messageQueue.shift()
this.wsManager.send(message.toJSON())
}
}
startHeartbeat() {
this.heartbeatManager = new HeartbeatManager(this.wsManager)
this.heartbeatManager.start()
}
disconnect() {
if (this.heartbeatManager) {
this.heartbeatManager.stop()
}
this.wsManager.close()
}
}
性能优化与调试技巧
消息压缩与批处理
class MessageBatcher {
constructor(batchSize = 10, batchTimeout = 100) {
this.batchSize = batchSize
this.batchTimeout = batchTimeout
this.batchQueue = []
this.batchTimer = null
}
addMessage(message) {
this.batchQueue.push(message)
if (this.batchQueue.length >= this.batchSize) {
this.flushBatch()
} else if (!this.batchTimer) {
this.batchTimer = setTimeout(() => {
this.flushBatch()
}, this.batchTimeout)
}
}
flushBatch() {
if (this.batchTimer) {
clearTimeout(this.batchTimer)
this.batchTimer = null
}
if (this.batchQueue.length === 0) return
const batch = this.batchQueue.splice(0, this.batchSize)
const compressed = this.compressBatch(batch)
// 发送批量消息
this.sendBatch(compressed)
}
compressBatch(messages) {
// 简单的JSON压缩
return JSON.stringify({
type: 'batch',
messages: messages.map(msg => ({
t: msg.type,
c: msg.content,
ts: msg.timestamp
}))
})
}
sendBatch(compressedData) {
// 实际发送逻辑
console.log('发送批量消息:', compressedData.length, 'bytes')
}
}
调试与监控
// WebSocket调试工具
class WebSocketDebugger {
static enableLogging = true
static log(event, data) {
if (!this.enableLogging) return
const timestamp = new Date().toISOString()
console.log(`[WebSocket ${timestamp}] ${event}:`, data)
}
static monitorPerformance(socketManager) {
const stats = {
messagesSent: 0,
messagesReceived: 0,
connectionTime: 0,
lastActivity: Date.now()
}
const originalSend = socketManager.send.bind(socketManager)
socketManager.send = function(data) {
stats.messagesSent++
stats.lastActivity = Date.now()
this.log('SEND', data)
return originalSend(data)
}
socketManager.socketTask.onMessage((res) => {
stats.messagesReceived++
stats.lastActivity = Date.now()
this.log('RECEIVE', res.data)
})
return stats
}
}
// 启用调试
WebSocketDebugger.enableLogging = true
安全考虑与最佳实践
安全连接
// 使用WSS协议
const useSecureWebSocket = true
const protocol = useSecureWebSocket ? 'wss' : 'ws'
// 证书验证
// #ifdef APP-PLUS
// App端可以自定义证书验证
const socketTask = uni.connectSocket({
url: 'wss://example.com/ws',
// 自定义TLS配置
tls: {
rejectUnauthorized: true,
// 自定义CA证书
【免费下载链接】uni-app A cross-platform framework using Vue.js 项目地址: https://gitcode.com/dcloud/uni-app
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



