Electron中单例WebSocket客户端的最佳放置位置及实现示例
Electron单例WebSocket客户端实现方案
最佳放置位置
直接放在**主进程(Main Process)**里是最优选择:
- 主进程本身就是单例运行,天然保证WebSocket客户端不会重复创建
- 主进程可直接访问Node.js API,方便写入本地存储(不管是用
fs模块还是Electron官方的electron-store) - 主进程和渲染进程通过IPC通信,既能把WebSocket收到的消息推给渲染进程,也能接收渲染进程的指令发送消息
核心实现思路
- 在主进程启动时初始化WebSocket客户端,做单例校验
- 监听WebSocket的
message事件,收到数据后直接写入本地存储 - 主进程注册IPC监听,接收渲染进程的发送消息请求,转发给WebSocket服务器
- 实现基础的重连逻辑,避免连接断开后无法恢复
完整代码示例
主进程代码(main.js)
const { app, BrowserWindow, ipcMain } = require('electron'); const Store = require('electron-store'); // 用electron-store做结构化本地存储,也可替换为fs模块 const WebSocket = require('ws'); // 初始化本地存储实例 const store = new Store(); // 单例WebSocket客户端实例 let wsClient = null; const WS_SERVER_URL = 'ws://your-server-address:port'; // 创建/重建WebSocket连接的方法 function initWebSocket() { // 如果已有活跃连接,直接返回 if (wsClient?.readyState === WebSocket.OPEN) return; wsClient = new WebSocket(WS_SERVER_URL); // 连接成功回调 wsClient.on('open', () => { console.log('WebSocket连接已建立'); }); // 接收服务器消息并写入本地存储 wsClient.on('message', (rawData) => { const message = JSON.parse(rawData.toString()); console.log('收到服务器消息:', message); // 按时间戳存储消息,避免覆盖 store.set(`ws_msg_${Date.now()}`, message); // 同步推送给所有打开的渲染窗口 BrowserWindow.getAllWindows().forEach(window => { window.webContents.send('ws-incoming-message', message); }); }); // 连接断开后自动重连 wsClient.on('close', () => { console.log('WebSocket连接断开,5秒后尝试重连'); setTimeout(initWebSocket, 5000); }); // 连接错误处理 wsClient.on('error', (err) => { console.error('WebSocket连接出错:', err); }); } // 创建应用窗口 function createMainWindow() { const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { // 实际项目建议开启contextIsolation,用contextBridge暴露IPC方法 nodeIntegration: true, contextIsolation: false } }); mainWindow.loadFile('index.html'); } // 应用就绪后初始化WebSocket和窗口 app.whenReady().then(() => { initWebSocket(); createMainWindow(); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createMainWindow(); }); }); // IPC监听:接收渲染进程的消息发送请求 ipcMain.on('ws-send-message', (event, payload) => { if (wsClient?.readyState === WebSocket.OPEN) { wsClient.send(JSON.stringify(payload)); console.log('已发送消息到服务器:', payload); } else { event.reply('ws-send-fail', 'WebSocket未连接'); console.error('WebSocket未连接,无法发送消息'); } }); // 关闭所有窗口时退出应用(macOS除外) app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit(); });
渲染进程代码(renderer.js)
const { ipcRenderer } = require('electron'); // 监听主进程推送的WebSocket消息 ipcRenderer.on('ws-incoming-message', (event, message) => { console.log('渲染进程收到消息:', message); // 更新页面显示 const messageList = document.getElementById('message-list'); messageList.innerHTML += `<div class="message-item">${JSON.stringify(message)}</div>`; }); // 发送消息到服务器的方法 function sendMessage() { const input = document.getElementById('msg-input'); const content = input.value.trim(); if (!content) return; const payload = { content, sendTime: Date.now() }; ipcRenderer.send('ws-send-message', payload); // 监听发送失败的反馈 ipcRenderer.once('ws-send-fail', (event, errMsg) => { alert(`发送失败:${errMsg}`); }); input.value = ''; }
页面示例(index.html)
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>WebSocket客户端</title> <style> .message-item { margin: 8px 0; padding: 8px; border-bottom: 1px solid #eee; } </style> </head> <body> <div id="message-list"></div> <div style="margin-top: 20px;"> <input type="text" id="msg-input" placeholder="输入消息内容" style="width: 600px; padding: 6px;"> <button onclick="sendMessage()" style="padding: 6px 12px;">发送</button> </div> <script src="./renderer.js"></script> </body> </html>
注意事项
- 本地存储选择:
electron-store适合结构化数据存储,自动处理JSON序列化;如果是大文件存储,建议用fs模块 - 重连优化:示例用固定间隔重连,实际可改为指数退避策略(比如第一次5秒,第二次10秒,最大间隔60秒)
- 安全规范:生产环境建议开启
contextIsolation,通过contextBridge暴露IPC方法,避免渲染进程直接访问Node.js API - 资源清理:应用退出前可主动调用
wsClient.close()关闭WebSocket连接,避免残留连接
内容的提问来源于stack exchange,提问作者BAR
相关产品推荐
相关产品推荐

