- 初始化所需的数据
const ws = useRef<WebSocket | null>(null)
// socket 数据
const [wsData, setMessage] = useState({})
// 锁定重连
let lockReconnect = false;
// 重连间隔
let tt: Timeout | null = null;
// socket 状态
const [readyState, setReadyState] = useState<any>({ key: 0, value: '正在连接中'})
//是否开发环境
const isDev = process.env.DEPLOY_ENV === 'dev';
//url地址
const url = isDev ? `ws://${param.host}:${param.port}${param.url}`
: 'ws://' + location.hostname + `:${param.port}${param.url}`;
- 创建websoket方法
const creatWebSocket = () => {
// 定义连接状态
const stateArr = [{key: 0, value: '正在连接中'},{key: 1, value: '已经连接并且可以通讯'},{key: 2, value: '连接正在关闭'},{key: 3, value: '连接已关闭或者没有连接成功'},]
try{
ws.current =new WebSocket(url)
ws.current.onopen = () => {setReadyState(stateArr[ws.current?.readyState ?? 0])}
ws.current.onclose = () => {setReadyState(stateArr[ws.current?.readyState ?? 0])}
ws.current.onerror = () => {setReadyState(stateArr[ws.current?.readyState ?? 0])}
ws.current.onmessage = (e) => {setMessage({...JSON.parse(e.data)})}
}
catch(error) {console.log(error)}}
3.定义方法
//初始化
const webSocketInit = () => {
if(!ws.current || ws.current.readyState === 3) {creatWebSocket()
}}
// 关闭 WebSocket
const closeWebSocket = () => {ws.current?.close()}
// 发送数据
const sendMessage = (str:string) => {ws.current?.send(str)}
//重连
const reconnect = () => {
try{
closeWebSocket()
ws.current = null
creatWebSocket()
}
catch(e) {console.log(e)}
4.监听ws
useEffect(() => {
webSocketInit()
return() => {ws.current?.close()
}}, [ws]`
5.根据需要交出所需要的数据或方法
return{
wsData,
readyState,
closeWebSocket,
reconnect,
sendMessage,
}
完整代码
interface urlConfig {
host: string;
port: string;
url: string;
}
import { useState, useRef, useEffect } from 'react';
const useWebsocket = (param: urlConfig) => {
const ws = useRef<WebSocket | null>(null);
// socket 数据
const [wsData, setMessage] = useState({});
// socket 状态
const [readyState, setReadyState] = useState<any>({ key: 0, value: '正在连接中' });
const isDev = process.env.DEPLOY_ENV === 'dev';
const url = isDev
? `ws://${param.host}:${param.port}${param.url}`
: 'ws://' + location.hostname + `:${param.port}${param.url}`;
const creatWebSocket = () => {
const stateArr = [
{ key: 0, value: '正在连接中' },
{ key: 1, value: '已经连接并且可以通讯' },
{ key: 2, value: '连接正在关闭' },
{ key: 3, value: '连接已关闭或者没有连接成功' },
];
try {
ws.current = new WebSocket(url);
ws.current.onopen = () => {
ws.current?.send('heartbeat');
setReadyState(stateArr[ws.current?.readyState ?? 0]);
};
ws.current.onclose = () => {
setReadyState(stateArr[ws.current?.readyState ?? 0]);
};
ws.current.onerror = () => {
setReadyState(stateArr[ws.current?.readyState ?? 0]);
};
ws.current.onmessage = (e) => {
setMessage({ ...JSON.parse(e.data) });
};
} catch (error) {
console.log(error);
}
};
const webSocketInit = () => {
if (!ws.current || ws.current.readyState === 3) {
creatWebSocket();
}
};
const closeWebSocket = () => {
ws.current?.close();
};
const sendMessage = (str: string) => {
ws.current?.send(str);
};
const reconnect = () => {
if (lockReconnect) {
return;
}
lockReconnect = true;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
tt && clearTimeout(tt);
//失败重连时间
tt = setTimeout(function () {
// console.log('重连中...');
lockReconnect = false;
closeWebSocket();
ws.current = null;
creatWebSocket();
}, 4000);
};
useEffect(() => {
webSocketInit();
return () => {
ws.current?.close();
};
}, []);
return {
wsData,
readyState,
closeWebSocket,
reconnect,
sendMessage,
};
}
使用
const [isLocalPage, setIsLocalPage] = useState(true);
const { wsData, readyState, closeWebSocket, reconnect } = useWebsocket({
host: process.env.WS_HOST as string,
port: process.env.WS_PORT as string,
url: process.env.WS_DEVICE_URL as string,
});
useEffect(() => {
if (Object.keys(wsData).length !== 0) {
const newDate = cloneDeep(data);
}
// 如果是已关闭且是当前页面自动重连
if (readyState.key === 3 && isLocalPage) {
reconnect();
}
// 不是当前页面 清空 webSocket 此处为优化代码使用的,不需要可以直接删除。
if (!isLocalPage) {
closeWebSocket();
}
}, [wsData, readyState, isLocalPage]);
// 判断是否当前页面
useEffect(() => {
document.addEventListener('visibilitychange', function () {
// 页面变为不可见时触发
if (document.visibilityState === 'hidden') {
setIsLocalPage(false);
}
// 页面变为可见时触发
if (document.visibilityState === 'visible') {
setIsLocalPage(true);
}
});
});
心跳检测
1.新建检测功能的class
/* eslint-disable @typescript-eslint/no-unused-expressions */
import type { Timeout } from 'ahooks/lib/useRequest/src/types';
class heartCheck {
timeout = 20000;
timeoutObj: Timeout | null = null;
serverTimeoutObj: Timeout | null = null;
websoket: WebSocket | undefined = undefined;
constructor(websoket: WebSocket) {
this.websoket = websoket;
}
reset() {
this.timeoutObj && clearTimeout(this.timeoutObj as Timeout);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj as Timeout);
}
start() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
this.reset();
this.timeoutObj = setTimeout(() => {
//20秒后发送心跳检测
(this.websoket as WebSocket).send('HeartBeat');
// console.log('ping');
self.serverTimeoutObj = setTimeout(() => {
//再过20秒后onmessage没有收到消息则直接关掉,触发重连
(this.websoket as WebSocket).close();
}, self.timeout);
}, this.timeout);
}
}
export default heartCheck;
心跳检测使用
1:在websocket实列化后创建心跳
ws.current = new WebSocket(url);
const heartbeat = new heartCheck(ws.current);
2:onopen和onmessage使用
heartbeat.start();
注意!和后端协商好回应的心跳字符串,避免影响正常数据
3:onclose使用
heartbeat.reset();