HcSocket.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import website from '~src/config/index'
  2. import { getObjValue, isNullES } from 'js-fast-way'
  3. // 长链接推送插件
  4. class HcSocket {
  5. constructor(data, change, open) {
  6. this.limit = website.socketLimit
  7. this.interval = website.socketInterval
  8. this.param = getObjValue(data)
  9. this.onChange = change
  10. this.onSocketOpen = open
  11. this.socket = null
  12. this.isReconnecting = false
  13. this.connectionStatus = 'disconnected'
  14. this.isManualClosed = false
  15. if (this.validateParams()) {
  16. this.create()
  17. //this.handleMultipleTabs()
  18. }
  19. }
  20. validateParams() {
  21. const { projectId, contractId, userId } = this.param
  22. const { socket: socketUrl, clientId } = website
  23. return !(isNullES(socketUrl) || isNullES(clientId) || isNullES(projectId) || isNullES(contractId) || isNullES(userId))
  24. }
  25. //创建连接
  26. create() {
  27. const { projectId, contractId, userId } = this.param
  28. const { socket: socketUrl, clientId } = website
  29. const url = `${socketUrl}/${clientId}/${projectId}/${contractId}/${userId}`
  30. this.socket = new WebSocket(url)
  31. this.socket.onopen = this.handleOpen.bind(this)
  32. this.socket.onclose = this.handleClose.bind(this)
  33. this.socket.onmessage = this.handleMessage.bind(this)
  34. this.socket.onerror = this.handleError.bind(this)
  35. }
  36. handleOpen() {
  37. console.log('WebSocket 链接成功')
  38. this.connectionStatus = 'connected'
  39. this.isManualClosed = false
  40. if (typeof this.onSocketOpen === 'function' && this.onSocketOpen) {
  41. this.onSocketOpen()
  42. }
  43. }
  44. handleClose() {
  45. console.log('WebSocket 链接已断开')
  46. this.connectionStatus = 'disconnected'
  47. if (!this.isManualClosed) {
  48. this.reconnect()
  49. }
  50. }
  51. handleMessage({ data }) {
  52. try {
  53. const parsedData = JSON.parse(data)
  54. if (typeof this.onChange === 'function') {
  55. this.onChange(parsedData)
  56. }
  57. } catch (error) {
  58. console.error('解析 WebSocket 数据时出错:', error)
  59. }
  60. }
  61. handleError(error) {
  62. console.error('WebSocket 错误:', error)
  63. this.connectionStatus = 'error'
  64. }
  65. //发送消息
  66. send(data) {
  67. if (this.isSocket()) {
  68. this.socket.send(data)
  69. } else {
  70. console.warn('消息发送失败')
  71. }
  72. }
  73. //链接是否存在
  74. isSocket() {
  75. return !isNullES(this.socket) && (this.socket.readyState === WebSocket.OPEN)
  76. }
  77. //尝试重连
  78. async reconnect(attempts = 0) {
  79. if (this.isReconnecting || this.isManualClosed) return
  80. this.isReconnecting = true
  81. while (attempts < this.limit && !this.isSocket() && !this.isManualClosed) {
  82. console.log(`重新链接中... (${attempts + 1}/${this.limit})`)
  83. this.create()
  84. await new Promise(resolve => setTimeout(resolve, this.getReconnectInterval(attempts)))
  85. attempts++
  86. }
  87. this.isReconnecting = false
  88. if (!this.isSocket() && !this.isManualClosed) {
  89. console.error('达到最大重连次数后仍然失败')
  90. }
  91. }
  92. // 指数退避策略
  93. getReconnectInterval(attempt) {
  94. return Math.min(1000 * Math.pow(2, attempt), this.interval)
  95. }
  96. handleMultipleTabs() {
  97. document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this))
  98. }
  99. // 处理多标签页问题
  100. handleVisibilityChange() {
  101. if (document.visibilityState === 'visible' && !this.isSocket() && !this.isManualClosed) {
  102. this.create()
  103. } else if (document.visibilityState === 'hidden') {
  104. this.close()
  105. }
  106. }
  107. //关闭或断开连接
  108. close() {
  109. this.isManualClosed = true
  110. if (this.isSocket()) {
  111. this.socket.close()
  112. }
  113. this.connectionStatus = 'disconnected'
  114. }
  115. destroy() {
  116. this.close()
  117. //document.removeEventListener('visibilitychange', this.handleVisibilityChange)
  118. this.socket = null
  119. this.onChange = null
  120. }
  121. }
  122. export { HcSocket }