useWebSocket.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import { reactive, ref, onBeforeUnmount } from 'vue';
  2. import { baseUrl, websocketPath } from '@/sheep/config';
  3. import { copyValueToTarget } from '@/sheep/util';
  4. export function useWebSocket(opt) {
  5. // 获取token
  6. const getAccessToken = () => {
  7. return uni.getStorageSync('token');
  8. };
  9. const options = reactive({
  10. url: (baseUrl + websocketPath).replace('http', 'ws') + '?token=' + getAccessToken(), // ws 地址
  11. isReconnecting: false, // 正在重新连接
  12. reconnectInterval: 3000, // 重连间隔,单位毫秒
  13. heartBeatInterval: 5000, // 心跳间隔,单位毫秒
  14. pingTimeoutDuration: 1000, // 超过这个时间,后端没有返回pong,则判定后端断线了。
  15. heartBeatTimer: null, // 心跳计时器
  16. destroy: false, // 是否销毁
  17. onConnected: () => {
  18. }, // 连接成功时触发
  19. onClosed: () => {
  20. }, // 连接关闭时触发
  21. onMessage: (data) => {
  22. }, // 收到消息
  23. });
  24. const Socket = ref(null); // Socket 链接实例
  25. const initEventListeners = () => {
  26. Socket.value.onOpen(() => {
  27. // WebSocket连接已打开
  28. options.onConnected();
  29. startHeartBeat();
  30. });
  31. Socket.value.onMessage((res) => {
  32. try {
  33. const obj = JSON.parse(res.data);
  34. if (obj.type === 'pong') {
  35. // 收到pong消息,心跳正常,无需处理
  36. resetPingTimeout(); // 重置计时
  37. } else {
  38. // 处理其他消息
  39. options.onMessage(obj);
  40. }
  41. } catch {
  42. console.error(res.data);
  43. }
  44. });
  45. Socket.value.onClose((res) => {
  46. // WebSocket连接已关闭
  47. if (options.destroy) {
  48. options.onClosed();
  49. return;
  50. }
  51. stopHeartBeat();
  52. if (!options.isReconnecting) {
  53. reconnect();
  54. }
  55. });
  56. };
  57. const sendMessage = (message) => {
  58. if (Socket.value) {
  59. Socket.value.send({
  60. data: message,
  61. });
  62. }
  63. };
  64. const startHeartBeat = () => {
  65. options.heartBeatTimer = setInterval(() => {
  66. sendMessage(JSON.stringify({
  67. type: 'ping',
  68. })); // 发送ping消息
  69. options.pingTimeout = setTimeout(() => {
  70. // 未收到pong消息,尝试重连...
  71. reconnect();
  72. }, options.pingTimeoutDuration);
  73. }, options.heartBeatInterval);
  74. };
  75. const stopHeartBeat = () => {
  76. if (options.heartBeatTimer) {
  77. clearInterval(options.heartBeatTimer);
  78. }
  79. };
  80. const reconnect = () => {
  81. options.isReconnecting = true;
  82. setTimeout(() => {
  83. onReconnect();
  84. initSocket();
  85. options.isReconnecting = false;
  86. }, options.reconnectInterval);
  87. };
  88. const resetPingTimeout = () => {
  89. clearTimeout(options.pingTimeout); // 重置计时
  90. };
  91. const close = () => {
  92. options.destroy = true;
  93. stopHeartBeat();
  94. if (Socket.value) {
  95. Socket.value.close();
  96. Socket.value = null;
  97. }
  98. };
  99. /**
  100. * 重连时触发
  101. */
  102. const onReconnect = () => {
  103. console.log('尝试重连...');
  104. };
  105. const initSocket = () => {
  106. copyValueToTarget(options, opt);
  107. Socket.value = uni.connectSocket({
  108. url: options.url,
  109. complete: () => {
  110. },
  111. });
  112. initEventListeners();
  113. };
  114. initSocket();
  115. onBeforeUnmount(()=>{
  116. close()
  117. })
  118. }