此教程基于uniapp提供的uniapp-tools的socket.js插件

1.创建socket.js(封装好的websocket连接对象)

const noop = function() {};
class Socket {
	static stopTime = 0;
	static concatCount = 0;
	constructor({
		url = '',
		onOpen = noop,
		onMsg = noop,
		onClose = noop,
		onError = noop,
		onReload = noop,
		onRdFinsh = noop,
		maxInterValCount = 10,
		interValTime = 2000,
		SocketState = {},
		...args
	} = {}) {
		this.isRconnectIng = false; //是否处于重连状态
		this.waiting = Promise.resolve(false); //心跳检查必须等待重连完成后
		this.waitDep = []; //等待时收集依赖的容器
		this.SocketTask = {
			nsend: noop,
			nclose: noop,
			nrconnect: noop,
			isconnect: false,
			uniColse: false,
			maxInterValCount,
			interValTime,
			InterValCount: 0,
			eventPatch: null,
			url,
			onOpen,
			onMsg,
			onClose,
			onError,
			onReload,
			onRdFinsh,
			extra: args
		};
		this._EventDispath(this.SocketTask);
		this.initChat(this.SocketTask, this.SocketTask.extra);
		return this.SocketTask;

	}
	set CONCATCOUNT(value) {
		Socket.concatCount = value;
		if (value > 0) this._notify();
	}
	get CONCATCOUNT() {
		return Socket.concatCount
	}
	/**
	 * 仅供内部使用,通知所有收集到的依赖
	 */
	_notify() {
		for (let i = 0; i < this.waitDep.length; i++) {
			this.waitDep[i].call(this.SocketTask);
		}
		this.waitDep = [];
	}
	/**
	 * 仅供内部使用,确认当前是否连接成功,收集依赖
	 */
	_chunkConnect(fn) {
		if (Socket.concatCount > 0) {
			fn();
		} else {
			this.waitDep.push(fn);
		}
	}
	/**
	 * 仅供内部使用,事件注册
	 */
	_EventDispath({
		onReload
	} = {}) {
		let SocketTask = this.SocketTask;
		let events = {
			onOpen: [],
			onMsg: [],
			onClose: [],
			onError: [],
			onReload: [],
			onRdFinsh: [],
		}
		SocketTask.nsend = text => {
			this._chunkConnect(() => {
				uni.sendSocketMessage({
					data: text
				})
			})
		}
		SocketTask.nclose = t => {
			this._chunkConnect(() => {
				SocketTask.uniColse = true;
				uni.closeSocket();
			})
		}
		SocketTask.nrconnect = t => {
			this._chunkConnect(() => {
				this.waiting = new Promise(async (resolve) => {
					uni.closeSocket();
					let reloadStatus = false;
					try {
						const res = await this.initChat(SocketTask, SocketTask.extra);
						reloadStatus = res;
					} catch (e) {}
					onReload.call(SocketTask, reloadStatus, SocketTask);
					SocketTask.eventPatch.dispatchEvent('onReload', reloadStatus);
					resolve(reloadStatus);
				})
			})
		}

		function EventDispatcher() {
			this.events = events;
		}
		for (let key in events) {
			EventDispatcher.prototype[key] = function(handler) {
				if (typeof handler != 'function') return;
				this.events[key].push(handler)
			}
		}
		EventDispatcher.prototype.dispatchEvent = function(type, msg) {
			let evenArr = this.events[type];
			if (evenArr.length > 0) {
				for (let i = 0; i < evenArr.length; i++) {
					evenArr[i].call(SocketTask, msg, SocketTask);
				}
			}
		}
		SocketTask.eventPatch = new EventDispatcher();
	}
	/**
	 * 心跳检测
	 */
	async hbDetection() {
		const SocketTask = this.SocketTask;
		if (SocketTask.uniColse) {
			return false;
		}
		clearTimeout(Socket.stopTime);
		if (!SocketTask.isconnect) { //未连接则启动连接
			if (SocketTask.maxInterValCount > SocketTask.InterValCount) {
				Socket.stopTime = setTimeout(async () => {
					try {
						const R_result = await this.waiting;
						if (R_result) return;
						this.isRconnectIng = true;
						const openResult = await this.initChat(SocketTask, SocketTask.extra);
						if (openResult) return;
						SocketTask.InterValCount++;
						return this.hbDetection();
					} catch (e) {
						return this.hbDetection();
					}
				}, SocketTask.interValTime)
			} else {
				SocketTask.onRdFinsh.call(SocketTask, SocketTask.maxInterValCount, SocketTask);
				SocketTask.eventPatch.dispatchEvent('onRdFinsh', SocketTask.maxInterValCount);
			}
		}
	}
	/**
	 * websocket监听事件
	 */
	SocketEvents({
		onOpen,
		onMsg,
		onClose,
		onError,
		onReload,
	} = {}) {
		return new Promise((resolve, reject) => {
			const SocketTask = this.SocketTask;
			uni.onSocketOpen(res => {
				this.CONCATCOUNT += 1;
				this.isRconnectIng = false;
				SocketTask.isconnect = true;
				SocketTask.InterValCount = 0;
				SocketTask.uniColse = false;
				resolve(true);
				onOpen.call(SocketTask, res, SocketTask);
				SocketTask.eventPatch.dispatchEvent('onOpen', res)
			})
			uni.onSocketMessage(msg => {
				onMsg.call(SocketTask, msg, SocketTask);
				SocketTask.eventPatch.dispatchEvent('onMsg', msg)
			})
			uni.onSocketClose(async err => {
				SocketTask.isconnect = false;
				resolve(false);
				if (!this.isRconnectIng) {
					this.hbDetection();
					onClose.call(SocketTask, err, SocketTask);
					SocketTask.eventPatch.dispatchEvent('onClose', err);
				}
			})
			uni.onSocketError(err => {
				uni.closeSocket();
				onError.call(SocketTask, err, SocketTask);
				SocketTask.eventPatch.dispatchEvent('onError', err)
			})
		})
	}
	/**
	 * 开始初始化chat
	 */
	initChat({
		url,
		onOpen,
		onMsg,
		onClose,
		onError,
		onReload
	} = {}, args) {
		return new Promise(async (resolve, reject) => {
			try {
				await this.connectSocket(url, args);
				let res = await this.SocketEvents({
					onOpen,
					onMsg,
					onClose,
					onError,
					onReload,
				})
				resolve(res);
			} catch (e) {
				console.log(e)
				reject();
			}
		})
	}
	/**
	 * 连接webSocket
	 */
	connectSocket(url, args) {
		return new Promise((resolve, reject) => {
			uni.connectSocket({
				url,
				success: () => {
					resolve();
				},
				fail: err => {
					reject();
				},
				...args
			})
		})
	}
}
export default Socket

2.创建useWebSocket.js

import Vue from 'vue'
import store from "./index.js"; //引入vuex,引入自己的路径
import socket from "../store/socket.js"; //引入socket.js,引入自己的路径
//这里将socket.js中的创建连接和接受消息的监听放到一个可以向外部暴露的方法,方便在合适的时机创建websocket连接
export function connectWebSocket(){
	const Socket =new socket({
	    url: 'ws://127.0.0.1:8086/ws', //连接地址 必填
	    onOpen(res) {
	        console.log('连接成功')
	    },
	    onClose(err) {
	        console.log('关闭了连接')
	    },
	    onReload(res) {
	        console.log('重载:' + res)
	    },
	    onMsg(msg) {
	        console.log(msg)
	    }
	});
    //加入监听,就可以全局使用监听了
    Socket.eventPatch.onOpen((msg,sk)=>{        //监听是否连接成功
    });
	Socket.eventPatch.onMsg((msg,sk)=>{    //监听是否接受消息
	    console.log(msg)
	});
	Vue.prototype.$Socket = Socket;    //连接成功挂在到原型上
}

3.修改 main.js

import Vue from 'vue'
import App from './App'

import store from '@/store' //引入自己的路径
import './store/useSocket.js' //引入自己的路径

Vue.prototype.$store=store
Vue.config.productionTip = false

App.mpType = 'app'

const app = new Vue({
    ...App
})
app.$mount()

4.修改vuex的js文件index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store =new Vuex.Store({
     state: {
        SocketState: {},
        SocketStateErr: {},
    },
	mutations:{
		setSocketState(that, info) {
			that.SocketState = info
		},
		setSocketStateErr(that, info) {
			that.SocketStateErr = info;
		}
	},
})
export default store

5.案例

1)Login.vue登录页面,主要看登录的实现
<template>
	<view>
		<h3>登录页</h3>
		<div>
			<view class="uni-padding-wrap uni-common-mt" style="padding:20px">
				<view class="button-sp-area">
					<button type="primary" plain="true"  @click="login('/pages/main/message/message')">登录</button>
				</view>
			</view>
		</div>
	</view>
</template>

<script>
	import {connectWebSocket} from "../../store/useSocket.js"; //引入socket.js 重要
	export default {
		methods: {
			login(url){
                connectWebSocket();
                var mobile="123456";
                //在跳转其他页面之前判断是否成功连接websocket
                this.$Socket.eventPatch.onOpen((msg,sk)=>{        //监听是否连接成功
                    console.log('连接成功')
                    //关闭其他页面,进入url页面
                    uni.reLaunch({
                        url:url+"?mobile="+mobile
                    })
                    this.$Socket.nsend(mobile)
                });
			}
		}
	}
</script>

2)message.vue
<template>
	<view>
		<div>
			<view class="uni-padding-wrap uni-common-mt" style="padding:20px">
				<view class="uni-form-item uni-column">
					<view class="title">收到的消息内容</view>
					<input class="uni-input" type="text" placeholder="xxx:xxxxx" :disabled="true" v-model="console" />
				</view>
				<br>
				<view class="uni-form-item uni-column">
					<view class="title">请输入朋友ID</view>
					<input class="uni-input" type="text" required placeholder="xxx" v-model="friendId" />
				</view>
				<br>
				<view class="uni-form-item uni-column">
					<view class="title">输入发送内容</view>
					<input class="uni-input" required placeholder="这个周日你有空吗" v-model="message" />
				</view>
				<br>
				<view class="button-sp-area">
					<button type="primary" plain="true" @click="sendMessage()">发送</button>
				</view>
			</view>
		</div>
		
	</view>
</template>

<script>
	export default {
		data() {
			return {
				myId:'',
				friendId:'',
				message:'',
				console:''
			}
		},
		onLoad(obj){
			this.$data.myId=obj.mobile;
		},
		onShow(){
            //页面渲染之后触发监听消息接收
			this.onmessage();
		},
		methods: {
			onmessage(){
                //当websocket收到后端发送的消息时,触发
				this.$Socket.eventPatch.onMsg((msg,sk)=>{    //监听是否接受消息
					var backMessage=msg.data.split(":");
					this.console="来自你的好友:"+backMessage[0]+","+"发来的消息:"+backMessage[1];
				});
			},
			sendMessage(){
				var mess=this.myId+","+this.friendId+","+this.message;
                //通过已经在登录页建立好的为websocket连接向后端发送消息
				this.$Socket.nsend(mess);
			}
		}
	}
</script>

<style>

</style>
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐