如果想实现实时更新,有四种方案

前言

最近在开发小程序的时候,遇到一个需求,需要更新页面数据再更新,如果接口数据不更新则不更新页面 通常来说实现这一功能,大都使用轮询+数据对比来实现 但是处理这种方法,我们还有四种方案可以更高效的来监控数据,实现这一功能

方案

  1. WebSocket 方案(推荐):
// websocket-service.js
export default {
  data() {
    return {
      ws: null,
      retryCount: 0,
      maxRetry: 3,
      heartbeatTimer: null
    }
  },
  
  methods: {
    initWebSocket() {
      try {
        this.ws = uni.connectSocket({
          url: 'your-websocket-url',
          success: () => {
            this.initWebSocketEvents()
          }
        })
      } catch (e) {
        console.error('WebSocket 连接失败:', e)
        this.reconnect()
      }
    },

    initWebSocketEvents() {
      this.ws.onOpen(() => {
        console.log('WebSocket 已连接')
        this.retryCount = 0
        this.startHeartbeat()
      })

      this.ws.onMessage((res) => {
        try {
          const data = JSON.parse(res.data)
          switch (data.type) {
            case 'pool_update':
              // 收到奖池更新通知,获取最新数据
              this.fetchLatestPoolData()
              break
            case 'pong':
              // 心跳响应
              break
            default:
              console.log('收到未知消息类型:', data.type)
          }
        } catch (e) {
          console.error('消息处理错误:', e)
        }
      })

      this.ws.onClose(() => {
        console.log('WebSocket 已断开')
        this.stopHeartbeat()
        this.reconnect()
      })

      this.ws.onError((error) => {
        console.error('WebSocket 错误:', error)
        this.reconnect()
      })
    },

    startHeartbeat() {
      this.heartbeatTimer = setInterval(() => {
        this.ws.send({
          data: JSON.stringify({ type: 'ping' })
        })
      }, 30000) // 每30秒发送一次心跳
    },

    stopHeartbeat() {
      if (this.heartbeatTimer) {
        clearInterval(this.heartbeatTimer)
        this.heartbeatTimer = null
      }
    },

    reconnect() {
      if (this.retryCount < this.maxRetry) {
        setTimeout(() => {
          console.log('尝试重新连接...')
          this.retryCount++
          this.initWebSocket()
        }, 3000 * this.retryCount) // 递增重试间隔
      } else {
        console.error('WebSocket 重连失败,切换到备用方案')
        this.switchToFallback()
      }
    },

    // 备用方案
    switchToFallback() {
      // 可以切换到 SSE 或轮询
      this.initSSE()
    }
  }
}
  1. SSE (Server-Sent Events) 方案:
// sse-service.js
export default {
  data() {
    return {
      eventSource: null,
      retryCount: 0,
      maxRetry: 3
    }
  },

  methods: {
    initSSE() {
      try {
        this.eventSource = new EventSource('your-sse-url')
        
        this.eventSource.onopen = () => {
          console.log('SSE 连接已建立')
          this.retryCount = 0
        }

        this.eventSource.onmessage = (event) => {
          try {
            const data = JSON.parse(event.data)
            if (data.type === 'pool_update') {
              this.fetchLatestPoolData()
            }
          } catch (e) {
            console.error('SSE 消息处理错误:', e)
          }
        }

        this.eventSource.onerror = (error) => {
          console.error('SSE 错误:', error)
          this.eventSource.close()
          this.reconnectSSE()
        }

      } catch (e) {
        console.error('SSE 初始化失败:', e)
        this.reconnectSSE()
      }
    },

    reconnectSSE() {
      if (this.retryCount < this.maxRetry) {
        setTimeout(() => {
          console.log('尝试重新连接 SSE...')
          this.retryCount++
          this.initSSE()
        }, 3000 * this.retryCount)
      } else {
        console.error('SSE 重连失败,切换到轮询')
        this.switchToPolling()
      }
    }
  }
}
  1. HTTP 长轮询(比普通轮询更高效):
// long-polling-service.js
export default {
  data() {
    return {
      polling: false,
      lastUpdateTime: null
    }
  },

  methods: {
    async startLongPolling() {
      if (this.polling) return
      this.polling = true

      while (this.polling) {
        try {
          const res = await this.waitForUpdate()
          if (res.hasUpdate) {
            await this.fetchLatestPoolData()
          }
        } catch (e) {
          console.error('长轮询错误:', e)
          // 出错后等待一段时间再重试
          await this.sleep(5000)
        }
      }
    },

    async waitForUpdate() {
      return new Promise((resolve, reject) => {
        uni.request({
          url: 'your-api/pool/wait-update',
          data: {
            lastUpdateTime: this.lastUpdateTime
          },
          // 设置较长的超时时间
          timeout: 30000,
          success: (res) => {
            if (res.data.code === 0) {
              this.lastUpdateTime = res.data.lastUpdateTime
              resolve(res.data)
            } else {
              reject(new Error(res.data.msg))
            }
          },
          fail: reject
        })
      })
    },

    stopLongPolling() {
      this.polling = false
    }
  }
}
  1. 混合方案(推荐):
// hybrid-update-service.js
export default {
  data() {
    return {
      currentStrategy: null,
      strategies: {
        WEBSOCKET: 'websocket',
        SSE: 'sse',
        LONG_POLLING: 'longPolling'
      }
    }
  },

  methods: {
    // 初始化更新监听
    initUpdateListener() {
      // 优先尝试 WebSocket
      this.tryStrategy(this.strategies.WEBSOCKET)
    },

    async tryStrategy(strategy) {
      this.currentStrategy = strategy
      
      switch (strategy) {
        case this.strategies.WEBSOCKET:
          try {
            await this.initWebSocket()
          } catch (e) {
            // WebSocket 失败,降级到 SSE
            this.tryStrategy(this.strategies.SSE)
          }
          break
          
        case this.strategies.SSE:
          try {
            await this.initSSE()
          } catch (e) {
            // SSE 失败,降级到长轮询
            this.tryStrategy(this.strategies.LONG_POLLING)
          }
          break
          
        case this.strategies.LONG_POLLING:
          this.startLongPolling()
          break
      }
    },

    // 根据网络状况动态调整策略
    async adjustStrategy() {
      const networkType = await this.getNetworkType()
      
      if (networkType === 'wifi' && this.currentStrategy !== this.strategies.WEBSOCKET) {
        // 网络状况好,尝试升级到 WebSocket
        this.tryStrategy(this.strategies.WEBSOCKET)
      } else if (networkType === 'none') {
        // 网络断开,停止所有监听
        this.stopAll()
      }
    }
  },

  // 监听网络状态变化
  onShow() {
    uni.onNetworkStatusChange((res) => {
      if (res.isConnected) {
        this.adjustStrategy()
      } else {
        this.stopAll()
      }
    })
  }
}

使用建议:

优先级顺序:

WebSocket > SSE > 长轮询 > 普通轮询

选择依据:

WebSocket:
实时性要求高,双向通信频繁
SSE:
只需服务器推送,客户端很少发送数据
长轮询:
简单可靠,对实时性要求不太高
普通轮询:
作为最后的备选方案

实现建议:

  • 实现优雅降级机制
  • 添加重试和错误处理
  • 考虑网络状况动态调整
  • 实现心跳检测
  • 添加断线重连功能

注意事项:

  • 考虑移动端网络特点
  • 处理应用前后台切换
  • 控制重连次数和间隔
  • 做好错误提示
  • 注意内存泄漏

需要根据具体场景选择合适的方案,或者采用混合方案以提供更好的用户体验。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值