该文章针对需要进行多个websocket连接,并且全局连接与页面单独连接做区分,并且页面单独连接的websocket可以针对具体连接进行关闭的功能 。若只有单个websocket连接需求,则参考另一篇文章uniapp websocket的封装与使用_uniapp 小程序 webscket 封装-CSDN博客

一、新建websocket.js文件

在common目录下新建websocket.js文件

class WebsocketUtil {
	constructor(url, time) {
		this.is_open_socket = false // 避免重复连接
		this.url = url // 地址
		this.data = null
		// 心跳检测
		this.timeout = time // 多少秒执行检测
		this.heartbeatInterval = null // 检测服务器端是否还活着
		this.reconnectInterval = null // 重连之后多久再次重连
		this.messageCallbacks = [] // 存储消息回调函数
		this.store = null // 存储Vuex store引用

		try {
			return this.connectSocketInit()
		} catch (e) {
			this.is_open_socket = false
		}
	}

	// 设置store
	setStore(store) {
		this.store = store
	}

	// 创建WebSocket连接
	connectSocketInit() {
		if (this.is_open_socket) return

		this.socketTask = uni.connectSocket({
			url: this.url,
			success: () => {
				this.is_open_socket = true
				return this.socketTask
			},
		})

		this.socketTask.onOpen((res) => {
			clearInterval(this.reconnectInterval)
			clearInterval(this.heartbeatInterval)
			this.is_open_socket = true
			this.start()

			this.socketTask.onMessage((res) => {
				try {
					const data = JSON.parse(res.data)
					// 如果有store,将数据存入store
					if (this.store) {
						this.store.commit('setWebSocketData', data)
					}
					// 执行所有注册的回调函数
					this.messageCallbacks.forEach(callback => callback(data))
				} catch (e) {
					console.error('WebSocket消息解析错误:', e)
				}
			})
		})

		this.socketTask.onError((res) => {
			this.is_open_socket = false
			if (this.socketTask) {
				this.socketTask.close()
			}
		})

		this.socketTask.onClose(() => {
			this.is_open_socket = false
		})
	}

	// 发送消息
	send(value) {
		if (!this.is_open_socket) return

		this.socketTask.send({
			data: JSON.stringify(value),
			success() {
				console.log("消息发送成功", value)
			},
			fail(err) {
				console.error("消息发送失败", err)
			}
		})
	}

	// 关闭连接
	close() {
		clearInterval(this.heartbeatInterval)
		clearInterval(this.reconnectInterval)
		this.is_open_socket = false
		if (this.socketTask) {
			this.socketTask.close()
		}
	}

	// 开启心跳检测
	start() {
		// this.heartbeatInterval = setInterval(() => {
		//   const heartbeatMsg = {
		//     type: 'heartbeat',
		//     userId: uni.getStorageSync('userinfo')?.user_id
		//   }
		//   this.send(heartbeatMsg)
		// }, this.timeout)
	}

	// 重新连接
	reconnect() {
		clearInterval(this.heartbeatInterval)
		if (!this.is_open_socket) {
			this.reconnectInterval = setInterval(() => {
				this.connectSocketInit()
			}, 3000)
		}
	}

	// 注册消息回调
	onMessage(callback) {
		this.messageCallbacks.push(callback)
	}

	// 移除消息回调
	offMessage(callback) {
		this.messageCallbacks = this.messageCallbacks.filter(cb => cb !== callback)
	}
}

class WebSocketManager {
	constructor() {
		this.connections = {}; // 存储所有WebSocket连接
		this.globalConnections = []; // 存储需要全局管理的连接名称
	}

	// 创建全局连接
	createGlobalConnection(name, url, time, store) {
		if (!this.connections[name]) {
			this.connections[name] = new WebsocketUtil(url, time);
			if (store) this.connections[name].setStore(store);
			this.globalConnections.push(name);
		}
		return this.connections[name];
	}

	// 创建页面级连接
	createPageConnection(name, url, time) {
		if (!this.connections[name]) {
			this.connections[name] = new WebsocketUtil(url, time);
		}
		return this.connections[name];
	}

	// 获取连接
	getConnection(name) {
		return this.connections[name];
	}

	// 关闭指定连接
	closeConnection(name) {
		console.log('关闭指定连接', name)
		if (this.connections[name]) {
			this.connections[name].close();
			delete this.connections[name];

			// 从全局连接列表中移除
			this.globalConnections = this.globalConnections.filter(n => n !== name);
		}
	}

	// 关闭所有全局连接
	closeAllGlobalConnections() {
		this.globalConnections.forEach(name => {
			this.closeConnection(name);
		});
	}

	// 关闭所有连接
	closeAllConnections() {
		Object.keys(this.connections).forEach(name => {
			this.connections[name].close();
		});
		this.connections = {};
		this.globalConnections = [];
	}
}

// 导出单例实例
export const webSocketManager = new WebSocketManager();
export default WebsocketUtil;

 二、调用方法

1、在全局调用 

在APP.vue中调用,按需调用,可调用多个不同链接的websocket,若只需调用一个,将ws2的删掉即可。注:store状态管理为接收到的websocket数据进行全局变量的设置,方便在其他页面进行调用

<script>
    //App.vue
	import {
		webSocketManager
	} from '@/common/websocket.js';
	import store from './store/index.js'
	export default {
		onLaunch() {
			// 初始化全局WebSocket连接
			webSocketManager.createGlobalConnection(
				'ws1',
				'你的websocket链接',
				30000,
				store
			);

			webSocketManager.createGlobalConnection(
				'ws2',
				'你的websocket链接',
				30000,
				store
			);

			// 注册全局消息处理
			webSocketManager.getConnection('ws1').onMessage((data) => {
				console.log('ws1收到消息:', data);
				// 处理消息逻辑
			});

			webSocketManager.getConnection('ws2').onMessage((data) => {
				console.log('ws2收到消息:', data);
				// 处理消息逻辑
			});
		},

		onShow() {
			// 检查并重连全局连接
			this.reconnectGlobalSockets();
		},

		onHide() {
			// 小程序进入后台时的处理
		},

		onUnload() {
			// 关闭所有全局连接
			webSocketManager.closeAllGlobalConnections();
		},

		methods: {
			reconnectGlobalSockets() {
				const globalConnections = ['ws1'];
				globalConnections.forEach(name => {
					const conn = webSocketManager.getConnection(name);
					if (!conn || !conn.is_open_socket) {
						webSocketManager.createGlobalConnection(
							name,
							conn ? conn.url : `wss://your-domain.com/${name}`,
							30000,
							store
						);
					}
				});
			}
		}
	}
</script>

<style>
	/*每个页面公共css */
</style>

2、在单个页面调用

<template>
	<view class="content">
		<text>详情页</text>
	</view>
</template>

<script>
	import {
		webSocketManager
	} from '@/common/websocket.js';

	export default {
		data() {
			return {
				detailSocket: null
			}
		},

		onLoad() {
			// 创建详情页专用的WebSocket连接
			this.detailSocket = webSocketManager.createPageConnection(
				'detailWs',
				'你的websocket链接',
				30000
			);

			// 注册消息回调
			this.detailSocket.onMessage((data) => {
				console.log('详情页收到消息:', data);
				// 处理消息逻辑
			});
		},

		onUnload() {
			if (this.detailSocket) {
				// 先移除所有消息回调
				this.detailSocket.messageCallbacks = this.detailSocket.messageCallbacks.filter(
					cb => cb !== this.handleDetailMessage
				);

				// 如果是独立连接才关闭
				if (webSocketManager.getConnection('detailWs')) {
					webSocketManager.closeConnection('detailWs');
				}

				this.detailSocket = null;
			}
		},

		methods: {
			sendDetailMessage(data) {
				if (this.detailSocket && this.detailSocket.is_open_socket) {
					this.detailSocket.send(data);
				} else {
					console.error('WebSocket连接未就绪');
				}
			},

			messageHandler(data) {
				// 专门处理详情页消息
			}
		}
	}
</script>

<style>
</style>

 3、关闭单个页面中指定websocket连接

webSocketManager.closeConnection('detailWs');

三、store状态管理

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    websocketData: null,
  },
  mutations: {
    setWebSocketData(state, data) {
      state.websocketData = data
    }
  },
  getters: {
    getWebSocketData: state => state.websocketData,
  }
})

export default store
//main.js

import App from './App'
import store from './store/index.js'
const app = new Vue({
	store,
  ...App
})

四、在页面中拿到websocket接收到数据

<script>
	import {
		mapState,
	} from 'vuex'

	export default {
		data() {
			return {
			}
		},
		computed: {
			...mapState(['websocketData'])
		},
		watch:{
			websocketData(newval,oldval){
				//监听数据变化
				console.log('WebSocket 数据变化:', newval)
			}
		},
	}
</script>

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐