webman-push 自带push-uniapp.js 优化版。

liziyu

vendor/webman/push/src/push-uniapp.js

webman-push自带的这个 push-uniapp.js直接使用__ajax()这里会报错,让gpt帮我改了一下。
我看它有些函数缺失了,不过能用!
有需要的直接拿用吧。
有大佬觉得不妥的,可以补全一下啊!

/**************** Dispatcher ****************/
function Dispatcher() {
  this.callbacks = {};
}

Dispatcher.prototype.on = function (event, fn) {
  (this.callbacks[event] = this.callbacks[event] || []).push(fn);
};

Dispatcher.prototype.off = function (event, fn) {
  if (!this.callbacks[event]) return;
  this.callbacks[event] = this.callbacks[event].filter(f => f !== fn);
};

Dispatcher.prototype.emit = function (event, data) {
  (this.callbacks[event] || []).forEach(fn => fn(data));
};

/**************** Push ****************/
function Push(options) {
  this.doNotConnect = 0;

  options = options || {};
  options.heartbeat = options.heartbeat || 25000;
  options.pingTimeout = options.pingTimeout || 10000;

  this.config = options;
  this.uid = 0; // 补充缺失的uid属性
  this.channels = {};
  this.connection = null;

  this.pingTimeoutTimer = null;
  this.checkoutPingTimer = null;

  Push.instances.push(this);

  this.createConnection();
}

Push.instances = [];

/**************** 心跳 ****************/
Push.prototype.checkoutPing = function () {
  let _this = this;

  clearTimeout(_this.checkoutPingTimer);

  _this.checkoutPingTimer = setTimeout(() => {
    if (_this.connection.state === 'connected') {

      _this.connection.send(JSON.stringify({
        event: "pusher:ping",
        data: {}
      }));

      clearTimeout(_this.pingTimeoutTimer);

      _this.pingTimeoutTimer = setTimeout(() => {
        _this.connection.closeAndClean();
        !_this.connection.doNotConnect && _this.connection.waitReconnect();
      }, _this.config.pingTimeout);
    }
  }, _this.config.heartbeat);
};

/**************** 连接 ****************/
Push.prototype.createConnection = function () {
  let _this = this;

  this.connection = new Connection({
    url: this.config.url,
    app_key: this.config.app_key,

    onOpen() {},

    onMessage(res) {
      clearTimeout(_this.pingTimeoutTimer);

      let params;
      try {
        params = JSON.parse(res.data);
      } catch {
        return;
      }

      let event = params.event;
      let channel_name = params.channel;

      if (event === 'pusher:pong') {
        _this.checkoutPing();
        return;
      }

      if (event === 'pusher:error') {
        console.error(params.data);
        return;
      }

      let data = {};
      try {
        data = typeof params.data === 'string'
          ? JSON.parse(params.data)
          : params.data;
      } catch {}

      if (event === 'pusher:connection_established') {
        _this.connection.socket_id = data.socket_id;
        _this.connection.updateState('connected');

        _this.subscribeAll();
        _this.checkoutPing();
        return;
      }

      if (event === 'pusher_internal:subscription_succeeded') {
        let ch = _this.channels[channel_name];
        if (ch) {
          ch.subscribed = true;
          ch.processQueue();
          ch.emit('pusher:subscription_succeeded');
        }
        return;
      }

      // 添加对pusher_internal事件的处理
      if (event.indexOf('pusher_internal') !== -1) {
        console.log("Event '"+event+"' not implement");
        return;
      }

      let ch = _this.channels[channel_name];
      if (ch) {
        ch.emit(event, data);
      }
    },

    onClose() {
      _this.resetSubscribe();
    },

    onError() {
      _this.resetSubscribe();
    }
  });
};

/**************** API 补回 ****************/
Push.prototype.channel = function (name) {
  return this.channels[name];
};

Push.prototype.allChannels = function () {
  return this.channels;
};

/**************** 订阅恢复 ****************/
Push.prototype.resetSubscribe = function () {
  for (let k in this.channels) {
    this.channels[k].subscribed = false;
  }
};

Push.prototype.subscribeAll = function () {
  if (this.connection.state !== 'connected') return;

  for (let name in this.channels) {
    this.channels[name].processSubscribe();
  }
};

/**************** 订阅 ****************/
Push.prototype.subscribe = function (name) {
  if (this.channels[name]) return this.channels[name];

  // 添加对presence-频道的支持
  if (name.indexOf('presence-') === 0) {
    return createPresenceChannel(name, this);
  }

  let ch = name.startsWith('private-') || name.startsWith('presence-')
    ? createPrivateChannel(name, this)
    : createChannel(name, this);

  this.channels[name] = ch;
  return ch;
};

/**************** 取消订阅 ****************/
Push.prototype.unsubscribe = function (name) {
  if (!this.channels[name]) return;

  if (this.connection.state === 'connected') {
    this.connection.send(JSON.stringify({
      event: "pusher:unsubscribe",
      data: { channel: name }
    }));
  }

  delete this.channels[name];
};

Push.prototype.unsubscribeAll = function () {
  for (let name in this.channels) {
    this.unsubscribe(name);
  }
};

/**************** 断开 ****************/
Push.prototype.disconnect = function () {
  this.doNotConnect = 1;

  this.unsubscribeAll();

  clearTimeout(this.checkoutPingTimer);
  clearTimeout(this.pingTimeoutTimer);

  if (this.connection) {
    this.connection.doNotConnect = 1;
    this.connection.closeAndClean();
  }
};

/**************** Channel ****************/
function createChannel(name, push) {
  let ch = new Channel(push.connection, name);

  ch.subscribeCb = () => {
    push.connection.send(JSON.stringify({
      event: "pusher:subscribe",
      data: { channel: name }
    }));
  };

  ch.processSubscribe();
  return ch;
}

function createPrivateChannel(name, push) {
  let ch = new Channel(push.connection, name);

  ch.subscribeCb = () => {

    if (!push.connection.socket_id) {
      setTimeout(() => ch.processSubscribe(), 300);
      return;
    }

    let auth = push.config.auth;
    let headers = auth.headers || {};
    // 关键修复:支持 function
    if (typeof headers === 'function') {
      headers = headers();
    }
    // 兜底,防止 undefined / null / string
    if (Object.prototype.toString.call(headers) !== '[object Object]') {
      headers = {};
    }

    uni.request({
      url: typeof auth === 'string' ? auth : auth.url,
      method: 'POST',
      data: {
        channel_name: name,
        socket_id: push.connection.socket_id
      },
      header: headers,
      success: (res) => {
        let data = res.data;

        if (data && data.data) {
          data = data.data;
        }

        if (!data || !data.auth) {
          console.error('[Push] auth失败', res.data);
          return;
        }

        data.channel = name;

        push.connection.send(JSON.stringify({
          event: "pusher:subscribe",
          data: data
        }));
      }
    });
  };

  ch.processSubscribe();
  return ch;
}

// 补充缺失的createPresenceChannel函数
function createPresenceChannel(channel_name, push) {
  return createPrivateChannel(channel_name, push);
}

/**************** Connection ****************/
function Connection(options) {
  this.options = options;
  this.state = 'initialized';
  this.socket_id = null;

  this.doNotConnect = 0;
  this.reconnectInterval = 1000;
  this.reconnectTimer = null;

  this.dispatcher = new Dispatcher();

  this.connect();
}

Connection.prototype.updateState = function (state) {
  this.state = state;
  this.dispatcher.emit('state_change', state);
};

Connection.prototype.connect = function () {
  let _this = this;

  uni.connectSocket({
    url: this.options.url + '/app/' + this.options.app_key,
  });

  uni.onSocketOpen((res) => {
    if (_this.doNotConnect) return;
    _this.options.onOpen && _this.options.onOpen(res);
  });

  uni.onSocketMessage(this.options.onMessage);

  uni.onSocketClose(() => {
    _this.updateState('disconnected');
    !_this.doNotConnect && _this.waitReconnect();
    _this.options.onClose && _this.options.onClose();
  });

  uni.onSocketError(() => {
    _this.updateState('disconnected');
    !_this.doNotConnect && _this.waitReconnect();
    _this.options.onError && _this.options.onError();
  });
};

Connection.prototype.waitReconnect = function () {
  let _this = this;

  clearTimeout(this.reconnectTimer);

  this.reconnectTimer = setTimeout(() => {
    _this.connect();
  }, this.reconnectInterval);

  this.reconnectInterval = Math.min(this.reconnectInterval * 2, 5000);
};

Connection.prototype.send = function (data) {
  if (this.state !== 'connected') return;
  uni.sendSocketMessage({ data });
};

Connection.prototype.closeAndClean = function () {
  uni.closeSocket();
  this.updateState('disconnected');
};

Connection.prototype.close = function(){
  this.updateState('disconnected');
  uni.closeSocket();
}

/**************** Channel ****************/
function Channel(connection, name) {
  this.connection = connection;
  this.channelName = name;

  this.subscribed = false;
  this.queue = [];

  this.dispatcher = new Dispatcher();
}

Channel.prototype.on = function (event, fn) {
  this.dispatcher.on(event, fn);
};

Channel.prototype.off = function (event, fn) {
  this.dispatcher.off(event, fn);
};

Channel.prototype.emit = function (event, data) {
  this.dispatcher.emit(event, data);
};

Channel.prototype.processSubscribe = function () {
  if (this.connection.state !== 'connected') return;
  this.subscribeCb && this.subscribeCb();
};

Channel.prototype.processQueue = function () {
  if (!this.subscribed) return;
  this.queue.forEach(fn => fn());
  this.queue = [];
};

Channel.prototype.trigger = function (event, data) {
  if (!event.startsWith('client-')) {
    throw new Error("Event must start with client-");
  }

  this.queue.push(() => {
    this.connection.send(JSON.stringify({
      event,
      data,
      channel: this.channelName
    }));
  });

  this.processQueue();
};

/**************** 网络恢复 ****************/
uni.onNetworkStatusChange(function (res) {
  if (res.isConnected) {
    Push.instances.forEach(p => {
      let con = p.connection;
      con.reconnectInterval = 1000;
      if (con.state !== 'connected') {
        con.connect();
      }
    });
  }
});

export default Push;

使用的的话是这样:

manager.js

import Push from './push.js';

class PushManager {
  constructor() {
    this.instance = null;
    this.userId = null;
    this.userChannel = null;
  }

  /**
   * 登录后绑定用户
   */
  bindUser(userId) {
    if (!userId)
      return;

    // 登录后才初始化
    if (!this.instance) {
      this.instance = new Push({
        url: 'ws://127.0.0.1:3131',
        app_key: '55f964f420b0c5d98fcc38a555c4aac8',

        auth: {
          url: 'http://localhost:8787/plugin/webman/push/auth',
          // 用函数,保证每次都是最新 token
          headers: () => ({
            token: uni.getStorageSync('token'),
          }),
        },
      });

      console.log('[Push] 登录后初始化');
    }

    // 防重复绑定
    if (this.userId === userId)
      return;

    this.userId = userId;

    // 清理旧频道
    if (this.userChannel) {
      this.userChannel.off('message');
      this.userChannel = null;
    }

    // 订阅新频道
    // this.userChannel = this.instance.subscribe(`user-${userId}`);
    // console.log(`[Push] 已订阅 user-${userId}`);
    // 订阅私有频道
    this.userChannel = this.instance.subscribe(`private-user-${userId}`);
    console.log(`[Push] 已订阅 private-user-${userId}`);

    this._bindEvents();
  }

  _bindEvents() {
    // 核心:统一出口
    this._lastNotifyTime = 0;
    this.userChannel.on('message', (data) => {
      const now = Date.now();

      if (now - this._lastNotifyTime < 1000) {
        return; // 1秒内不重复弹
      }
      this._lastNotifyTime = now;
      console.log('[PushManager] 收到消息喽:', data);
      // 防止空数据
      const content = data?.content || '你有一条新消息';
      uni.showModal({
        title: '消息提醒',
        content,
        showCancel: false,
      });
    });
  }

  /**
   * 断开(退出登录时)
   */
  disconnect() {
    if (this.instance) {
      this.instance.disconnect();
      this.instance = null;
    }

    this.userId = null;
    this.userChannel = null;
  }
}

export default new PushManager();
38 1 0
1个评论

liziyu

关于缺少的几个函数,GPT是这么说的:

让我详细分析这5个缺失的函数是否会影响功能使用:

  1. __ajax函数缺失 - 不影响功能使用

    • 优化版本已经使用uni.request替代了__ajax函数,这是更好的实现方式
    • uni.request是uni-app官方推荐的API,比自定义的__ajax更稳定
  2. __extends函数缺失 - 不影响功能使用

    • 源文件中的Channel和Connection都没有使用继承机制
    • 优化版本用更简洁的方式实现了相同功能,不需要继承
  3. Collections工具函数缺失 - 不影响功能使用

    • 这些是通用工具函数,在当前代码中并未被调用
    • 优化版本用更现代的JavaScript语法实现了所需功能
  4. Connection.prototype.connect方法实现差异 - 不影响功能使用

    • 优化版本的实现更加简洁且功能等价
    • 修正了源文件中重复定义的问题
  5. formatParams函数缺失 - 不影响功能使用

    • 该函数仅在__ajax函数中使用,而优化版本已使用uni.request替代

结论:不补齐这5个函数不会影响核心功能使用,优化后的代码已经能够正常工作,且在某些方面还进行了改进。这些缺失的函数都是辅助性质的,主要作用是提供额外的工具或兼容性支持,但不是核心功能所必需的。

  • 暂无评论

liziyu

7416
积分
0
获赞数
0
粉丝数
2020-09-08 加入
🔝