Stomp 订阅模块化

版本

  • "vite": "7.1.5",
  • "@stomp/rx-stomp": "2.0.1 ",
  • "sockjs-client": "1.6.1",

目录结构

  • subscribe
    • modules
      • useMessageSubscribe.ts [消息订阅]
      • useOnlineUsersSubscribe.tsx [用户在线状态订阅]
    • index.ts [主逻辑]
    • key.ts [Destination变量]

keys

ts 复制代码
// 消息
export const MESSAGE_KEY = '/user/message';

// 在线用户
export const ONCE_ONLINE_USERS_KEY = '/user/topic/online/user';

useOnlineUsersSubscribe

ts 复制代码
import { defineSubscribe } from '~/subscribe';
import { ONCE_ONLINE_USERS_KEY } from '../key';

export const useOnlineUsersSubscribe = defineSubscribe(
  ONCE_ONLINE_USERS_KEY,
  ({ action, data: contact }) => {
    console.log('useOnlineUsersSubscribe: ', action, contact);
    // TODO ....
  }
);

useMessageSubscribe

ts 复制代码
import { defineSubscribe } from '~/subscribe';
import { MESSAGE_KEY } from '../key';

export const useMessageSubscribe = defineSubscribe<MessageEntity>(
  MESSAGE_KEY,
  (message, rawMessage) => {
   // TODO 
  }
);

创建 STOMP 连接

ts 复制代码
import type { App } from 'vue';

export default (_: App) => {
  console.log('Stomp: 插件进行加载中');
  const accessToken = 'xx'
  createRxStomp({
    brokerURL: 'http://localhost:8080/ws',
    heartbeatIncoming: 2000,
    heartbeatOutgoing: 2000,
    reconnectDelay: 5000,
    connectHeaders: {
      Authorization: `Bearer ${accessToken}`,
    },
    debug: (str) => {
      console.log(str);
    },
  });
};

index.ts

主要内容在 loadDirectorySubscriptions 方法下, 通过 import.meta.glob 获取所有 Subscribe结尾的 js、ts、tsx 模块暴露的方法并调用

ts 复制代码
import { IMessage, RxStomp, RxStompState } from '@stomp/rx-stomp';
import SockJS from 'sockjs-client/dist/sockjs.min.js';
import type { RxStompConfig } from '@stomp/rx-stomp/src/rx-stomp-config';
import { StompHeaders } from '@stomp/stompjs/src/stomp-headers';

let rxStompInstance: RxStomp | null;
const subscribeModules = import.meta.glob(
  '~/subscribe/**/*Subscribe.{ts,tsx,js}'
);

interface StompOptions extends RxStompConfig {
  authenticate?: string;
  include?: string;
}

export const createRxStomp = ({
  brokerURL,
  authenticate,
  beforeConnect,
  connectHeaders,
  ...option
}: StompOptions) => {
  // 存在复用
  if (rxStompInstance) {
    return rxStompInstance;
  }

  rxStompInstance = new RxStomp();
  rxStompInstance.configure({
    webSocketFactory: () => new SockJS(brokerURL || 'http://localhost:8080/ws'),
    connectHeaders: {
      ...connectHeaders,
    },
    ...option,
  });
  // 激活连接
  rxStompInstance.activate();

  console.log('Stomp: 默认连接头', rxStompInstance.stompClient.connectHeaders);

  // 加载目录下所有订阅
  loadDirectorySubscriptions();
  return rxStompInstance;
};

export const useRxStomp = () => {
  if (!rxStompInstance) {
    throw new Error(
      'RxStomp instance not created. Use createRxStomp to create an instance first.'
    );
  }
  return rxStompInstance;
};

/**
 * 订阅
 */
export const defineSubscribe = <T>(
  topic: string,
  callback: (
    message: T,
    rawMessage: IMessage
  ) => void
) => {
  const rxStomp = useRxStomp();
  return rxStomp.watch(topic).subscribe((message) => {
    callback(JSON.parse(message.body), {
      ...message,
    });
  });
};

/**
 * 加载目录下所有订阅
 */
export const loadDirectorySubscriptions = async () => {
  /**
   * 从路径中提取模块名称
   *
   * @param filePath 文件路径
   * @returns 模块名称
   */
  const extractModuleName = (filePath: string): string => {
    // 使用正则表达式匹配文件名部分并去掉扩展名
    const match = /\/([^/]+)\.([tj]sx?)$/.exec(filePath);
    if (match && match[1]) {
      return match[1];
    }
    throw new Error(`无法从路径中提取模块名称: ${filePath}`);
  };

  for (const path in subscribeModules) {
    await subscribeModules[path]();
    console.log(`🧩 Scan Subscribe: ${extractModuleName(path)}`);
  }
};

/**
 * 刷新连接
 *
 * @param headers
 */
export const refreshConnectHeaders = async (headers: StompHeaders) => {
  if (!rxStompInstance) {
    throw new Error('STOMP实例未初始化');
  }

  // 断开连接
  await rxStompInstance.deactivate();

  // 更新连接头
  rxStompInstance.stompClient.configure({
    connectHeaders: {
      ...rxStompInstance.stompClient.connectHeaders,
      ...headers,
    },
  });

  // 重新请求
  rxStompInstance.activate();

  console.log(
    'Stomp: 连接头已更新',
    rxStompInstance.stompClient.connectHeaders
  );
};
相关推荐
千码君20161 天前
React Native:使用vite创建react项目并熟悉react语法
javascript·css·react native·react.js·html·vite·jsx
雪山上的小灰熊1 天前
UNIAPP如何自定义全局方法?
javascript·typescript·uni-app·vue·vue3·vite·hooks
老坛程序员2 天前
FastAPI WebSocket 由浅入深的开发范例
websocket·网络协议·fastapi
weixin_436525072 天前
芋道源码 - RabbitMQ + WebSocket 实现分布式消息推送
分布式·websocket·rabbitmq
小样还想跑2 天前
UniApp ConnectSocket连接websocket
websocket·elasticsearch·uni-app
FreeBuf_2 天前
Zloader木马再次升级:通过DNS隧道和WebSocket C2实现更隐蔽的攻击
websocket·网络协议·php
太阳伞下的阿呆2 天前
Http与WebSocket
websocket·网络协议·http
luckys.one3 天前
第12篇|[特殊字符] Freqtrade 交易所接入全解:API、WebSocket、限频配置详解
网络·ide·python·websocket·网络协议·flask·流量运营