YingXingAI/docs/WebSocket使用示例.md

608 lines
12 KiB
Markdown
Raw Normal View History

# WebSocket 使用示例
## 📦 已创建的文件
`/utils/websocket-manager.js` - WebSocket 管理器(已完成)
---
## 🚀 快速开始
### 1. 在 App.vue 中初始化连接
```vue
<script>
import wsManager from '@/utils/websocket-manager.js'
import { mapState } from 'vuex'
export default {
computed: {
...mapState(['vuex_token', 'vuex_user'])
},
onLaunch() {
console.log('App Launch')
// 如果已登录,连接 WebSocket
if (this.vuex_token) {
this.connectWebSocket()
}
},
methods: {
connectWebSocket() {
// 替换为你的 WebSocket 服务器地址
const wsUrl = `wss://your-server.com/chat?token=${this.vuex_token}`
wsManager.connect(wsUrl, {
reconnectInterval: 3000, // 重连间隔 3 秒
maxReconnectAttempts: 10, // 最多重连 10 次
heartbeatInterval: 30000 // 心跳间隔 30 秒
})
// 监听连接成功
wsManager.on('open', () => {
console.log('[App] WebSocket 连接成功')
uni.showToast({
title: '已连接',
icon: 'success'
})
})
// 监听连接关闭
wsManager.on('close', (res) => {
console.log('[App] WebSocket 已关闭', res)
})
// 监听连接错误
wsManager.on('error', (err) => {
console.error('[App] WebSocket 错误', err)
uni.showToast({
title: '连接失败',
icon: 'none'
})
})
// 监听重连
wsManager.on('reconnect', (attempts) => {
console.log(`[App] 正在重连 (${attempts})`)
})
// 监听消息(全局消息处理)
wsManager.on('message', (data) => {
console.log('[App] 收到消息:', data)
// 根据消息类型处理
switch (data.type) {
case 'message':
// 新消息
this.handleNewMessage(data)
break
case 'notification':
// 系统通知
this.handleNotification(data)
break
default:
console.log('[App] 未知消息类型:', data.type)
}
})
},
handleNewMessage(data) {
// 更新 Vuex 中的消息列表
this.$store.commit('addMessage', data)
// 显示新消息提示
uni.showTabBarRedDot({
index: 1 // 消息页的 tabBar 索引
})
},
handleNotification(data) {
uni.showToast({
title: data.content,
icon: 'none'
})
}
},
onHide() {
// App 进入后台,保持连接
console.log('App Hide')
},
onShow() {
// App 从后台回到前台
console.log('App Show')
// 如果连接已断开,重新连接
const state = wsManager.getState()
if (!state.isConnected && !state.isConnecting) {
this.connectWebSocket()
}
}
}
</script>
```
---
### 2. 在聊天页面中使用
#### 方式 A修改现有的 `dialogBox.vue`
```vue
<template>
<view>
<!-- 消息列表 -->
<scroll-view
:scroll-top="scrollTop"
:scroll-with-animation="true"
class="scroll"
scroll-y="true"
>
<view v-for="(msg, index) in messageList" :key="index" class="message-item">
<!-- 我方消息 -->
<view v-if="msg.fromUserId == vuex_user.id" class="my-message">
<text>{{ msg.content }}</text>
</view>
<!-- 对方消息 -->
<view v-else class="other-message">
<text>{{ msg.content }}</text>
</view>
</view>
</scroll-view>
<!-- 输入框 -->
<view class="input-box">
<input
v-model="inputValue"
placeholder="请输入消息"
@confirm="sendMessage"
/>
<button @click="sendMessage">发送</button>
</view>
</view>
</template>
<script>
import wsManager from '@/utils/websocket-manager.js'
import { mapState } from 'vuex'
export default {
data() {
return {
messageList: [],
inputValue: '',
scrollTop: 0,
targetUserId: '', // 对方用户 ID
messageHandler: null
}
},
computed: {
...mapState(['vuex_user'])
},
onLoad(options) {
// 获取对方用户 ID
this.targetUserId = options.userId || ''
// 加载历史消息
this.loadHistoryMessages()
// 监听新消息
this.messageHandler = (data) => {
// 只处理与当前聊天对象相关的消息
if (data.fromUserId === this.targetUserId || data.toUserId === this.targetUserId) {
this.messageList.push({
fromUserId: data.fromUserId,
toUserId: data.toUserId,
content: data.content,
timestamp: data.timestamp
})
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom()
})
// 发送已读回执
this.sendReadReceipt(data.messageId)
}
}
wsManager.on('message', this.messageHandler)
},
onUnload() {
// 移除消息监听
if (this.messageHandler) {
wsManager.off('message', this.messageHandler)
}
},
methods: {
// 加载历史消息
async loadHistoryMessages() {
try {
const res = await this.$u.api.getMessages({
userId: this.targetUserId,
page: 1,
pageSize: 50
})
this.messageList = res.data || []
this.$nextTick(() => {
this.scrollToBottom()
})
} catch (e) {
console.error('加载历史消息失败:', e)
}
},
// 发送消息
sendMessage() {
if (!this.inputValue.trim()) {
uni.showToast({
title: '请输入消息',
icon: 'none'
})
return
}
// 通过 WebSocket 发送
wsManager.send({
type: 'message',
fromUserId: this.vuex_user.id,
toUserId: this.targetUserId,
content: this.inputValue,
timestamp: Date.now()
})
// 立即显示到界面(乐观更新)
this.messageList.push({
fromUserId: this.vuex_user.id,
toUserId: this.targetUserId,
content: this.inputValue,
timestamp: Date.now(),
sending: true // 标记为发送中
})
// 清空输入框
this.inputValue = ''
// 滚动到底部
this.$nextTick(() => {
this.scrollToBottom()
})
},
// 发送已读回执
sendReadReceipt(messageId) {
wsManager.send({
type: 'read',
messageId: messageId,
userId: this.vuex_user.id
})
},
// 滚动到底部
scrollToBottom() {
this.scrollTop = 99999
}
}
}
</script>
```
---
### 3. 在 Vuex 中管理 WebSocket 状态(可选)
```javascript
// store/index.js
export default new Vuex.Store({
state: {
// ... 其他状态
wsConnected: false,
unreadCount: 0,
messages: []
},
mutations: {
// WebSocket 连接状态
SET_WS_CONNECTED(state, connected) {
state.wsConnected = connected
},
// 新消息
ADD_MESSAGE(state, message) {
state.messages.push(message)
state.unreadCount++
},
// 清空未读
CLEAR_UNREAD(state) {
state.unreadCount = 0
}
},
actions: {
// 在 App.vue 中调用
wsConnected({ commit }) {
commit('SET_WS_CONNECTED', true)
},
wsDisconnected({ commit }) {
commit('SET_WS_CONNECTED', false)
}
}
})
```
---
## 📋 消息协议设计
### 客户端 → 服务器
#### 1. 发送文字消息
```json
{
"type": "message",
"fromUserId": "user123",
"toUserId": "user456",
"content": "你好",
"timestamp": 1635678901234
}
```
#### 2. 发送心跳
```json
{
"type": "ping",
"timestamp": 1635678901234
}
```
#### 3. 发送已读回执
```json
{
"type": "read",
"messageId": "msg123",
"userId": "user123"
}
```
---
### 服务器 → 客户端
#### 1. 推送消息
```json
{
"type": "message",
"messageId": "msg123",
"fromUserId": "user456",
"toUserId": "user123",
"content": "你好",
"timestamp": 1635678901234
}
```
#### 2. 心跳响应
```json
{
"type": "pong",
"timestamp": 1635678901234
}
```
#### 3. 系统通知
```json
{
"type": "notification",
"content": "系统维护通知",
"timestamp": 1635678901234
}
```
#### 4. 错误消息
```json
{
"type": "error",
"code": 401,
"message": "未授权",
"timestamp": 1635678901234
}
```
---
## 🔧 后端实现参考
### Node.js + ws
```javascript
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8080 })
// 存储所有连接(用户 ID -> WebSocket
const clients = new Map()
wss.on('connection', (ws, req) => {
// 从 URL 获取 token
const token = new URL(req.url, 'http://localhost').searchParams.get('token')
// 验证 token获取用户 ID
const userId = verifyToken(token)
if (!userId) {
ws.close(4001, '未授权')
return
}
// 保存连接
clients.set(userId, ws)
console.log(`用户 ${userId} 已连接`)
// 处理消息
ws.on('message', (message) => {
const data = JSON.parse(message)
switch (data.type) {
case 'message':
// 转发消息
handleMessage(data)
break
case 'ping':
// 心跳响应
ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }))
break
case 'read':
// 已读回执
handleReadReceipt(data)
break
}
})
// 处理断开
ws.on('close', () => {
clients.delete(userId)
console.log(`用户 ${userId} 已断开`)
})
})
// 转发消息
function handleMessage(data) {
const { toUserId, fromUserId, content } = data
// 保存到数据库
saveMessage({ fromUserId, toUserId, content })
// 推送给接收者
const targetWs = clients.get(toUserId)
if (targetWs && targetWs.readyState === WebSocket.OPEN) {
targetWs.send(JSON.stringify({
type: 'message',
messageId: generateId(),
fromUserId,
toUserId,
content,
timestamp: Date.now()
}))
}
}
```
---
## 📊 完整流程图
```
┌─────────────┐
│ App 启动 │
└──────┬──────┘
┌─────────────┐
│ 检查登录状态 │
└──────┬──────┘
▼ (已登录)
┌─────────────┐
│ 连接 WS │ ← wsManager.connect()
└──────┬──────┘
┌─────────────┐
│ 监听消息 │ ← wsManager.on('message')
└──────┬──────┘
├─→ (收到消息) → 更新 UI
└─→ (发送消息) → wsManager.send()
┌─────────────┐
│ 服务器处理 │
└──────┬──────┘
┌─────────────┐
│ 推送给对方 │
└─────────────┘
```
---
## ✅ 测试清单
### 功能测试
- [ ] 连接成功
- [ ] 发送消息
- [ ] 接收消息
- [ ] 心跳正常
- [ ] 手动断开
- [ ] 自动重连
- [ ] 消息队列
### 场景测试
- [ ] App 切换到后台
- [ ] App 从后台恢复
- [ ] 网络断开
- [ ] 网络恢复
- [ ] 服务器重启
- [ ] 并发多人聊天
### 性能测试
- [ ] 消息延迟 < 200ms
- [ ] 重连时间 < 5s
- [ ] 内存占用正常
- [ ] CPU 占用正常
---
## 🎉 总结
现在你已经有:
1.**WebSocket 管理器** (`utils/websocket-manager.js`)
- 自动重连
- 心跳检测
- 消息队列
- 事件监听
2.**使用示例**
- App.vue 初始化
- 聊天页面使用
- Vuex 状态管理
3.**消息协议**
- 客户端 → 服务器
- 服务器 → 客户端
4.**后端参考**
- Node.js + ws 实现
---
## 🚀 下一步
1. **确认后端技术栈**
- 告诉我你用什么后端,我帮你写完整的服务端代码
2. **集成到现有聊天页面**
- 我帮你修改 `dialogBox.vue`
3. **添加更多功能**
- 图片消息
- 语音消息
- 消息已读
- 输入中状态
随时告诉我需要什么!🎯