多页面共享pinia中的WebSocket消息

476 阅读2分钟

在多页面场景中,使用 Pinia 来共享 WebSocket 消息非常适合,因为 Pinia 是全局的状态管理工具,所有页面或组件都可以访问同一个状态,实现消息共享。

实现思路

  1. 通过 Pinia 管理 WebSocket 实例及其数据: 将 WebSocket 的逻辑封装在一个 Pinia Store 中。
  2. 所有页面或组件订阅相同的 Pinia Store: 通过 Pinia 的状态更新,页面共享数据。
  3. 集中式消息处理: 将消息解析和分发逻辑放在 Store 中,避免重复处理。

实现步骤

1. 创建 WebSocket Store

创建一个用于管理 WebSocket 的 Pinia Store,比如 stores/websocket.js

import { defineStore } from 'pinia';

export const useWebSocketStore = defineStore('websocket', {
  state: () => ({
    ws: null,          // WebSocket 实例
    isConnected: false, // WebSocket 连接状态
    message: null,      // 最新收到的消息
    messages: [],       // 消息列表(可选)
  }),
  actions: {
    connect(url) {
      if (this.ws) {
        console.warn('WebSocket already connected.');
        return;
      }

      this.ws = new WebSocket(url);

      this.ws.onopen = () => {
        this.isConnected = true;
        console.log('WebSocket connected');
      };

      this.ws.onmessage = (event) => {
        const data = JSON.parse(event.data);
        this.message = data; // 更新最新消息
        this.messages.push(data); // 保存历史消息(可选)
      };

      this.ws.onclose = () => {
        this.isConnected = false;
        this.ws = null;
        console.error('WebSocket closed. Reconnecting...');
        setTimeout(() => this.connect(url), 3000); // 自动重连
      };

      this.ws.onerror = (err) => {
        console.error('WebSocket error:', err);
      };
    },
    sendMessage(data) {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify(data));
      } else {
        console.error('WebSocket is not open.');
      }
    },
    close() {
      if (this.ws) {
        this.ws.close();
        this.ws = null;
        this.isConnected = false;
      }
    },
  },
});

2. 在应用启动时初始化 WebSocket

在应用的主入口文件 main.js 中初始化 WebSocket:

// main.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import { useWebSocketStore } from './stores/websocket';

const app = createApp(App);
const pinia = createPinia();

app.use(pinia);

// 初始化 WebSocket
const wsStore = useWebSocketStore();
wsStore.connect('wss://your-websocket-url');

app.mount('#app');

3. 在页面或组件中使用 WebSocket 数据

每个页面或组件可以直接访问 WebSocketStore,实时共享最新的消息和状态。

<template>
  <div>
    <h1>WebSocket Data</h1>
    <p>Connection Status: {{ isConnected ? 'Connected' : 'Disconnected' }}</p>
    <p>Latest Message: {{ message }}</p>
    <ul>
      <li v-for="(msg, index) in messages" :key="index">
        {{ msg }}
      </li>
    </ul>
    <button @click="sendPing">Send Ping</button>
  </div>
</template>

<script>
import { useWebSocketStore } from '@/stores/websocket';

export default {
  setup() {
    const wsStore = useWebSocketStore();

    const sendPing = () => {
      wsStore.sendMessage({ type: 'ping', timestamp: Date.now() });
    };

    return {
      isConnected: wsStore.isConnected,
      message: wsStore.message,
      messages: wsStore.messages,
      sendPing,
    };
  },
};
</script>

4. 支持多页面共享的关键点

  1. Pinia 是全局共享的: 每个页面访问的是同一个 WebSocketStore 实例,因此状态(messagemessages 等)始终一致。
  2. 组件不需重复创建 WebSocket: 通过在 main.js 中初始化 WebSocket,确保整个应用只创建一个 WebSocket 实例。
  3. 实时数据同步: 当 Store 的 message 更新时,所有订阅页面会立即响应。

优化建议

  1. 消息分发: 如果 WebSocket 返回的数据需要分发到不同模块,可以在 onmessage 中根据数据类型调用其他 Store 的方法。例如:

    const someStore = useSomeStore();
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      if (data.type === 'notification') {
        someStore.addNotification(data);
      }
    };
    
  2. 使用持久化插件: 如果需要在页面刷新时保留历史消息(messages),可以使用 Pinia 的持久化插件(如 pinia-plugin-persist)。

    import piniaPluginPersistedState from 'pinia-plugin-persistedstate';
    
    const pinia = createPinia();
    pinia.use(piniaPluginPersistedState);
    
  3. 性能优化: 对频繁更新的消息进行节流或防抖处理,避免组件过度渲染。


这样,每个页面都可以方便地共享和实时获取 WebSocket 的消息!