Electron 43.2.0 已发布。这次更新中值得桌面应用开发者关注的一项能力,是新增 WHATWG 兼容的 net.WebSocket。Electron 应用可以在主进程中使用更接近浏览器标准的 WebSocket 接口,同时继续依托 Electron 的网络栈处理连接。
为什么主进程 WebSocket 值得关注
Electron 基于 Chromium 和 Node.js,可用 JavaScript、HTML 与 CSS 构建 macOS、Windows 和 Linux 桌面应用。实际项目通常同时存在两类网络代码:渲染进程使用浏览器 API,主进程则使用 Node.js 库。
新增的 net.WebSocket 缩小了这两套编程模型之间的差异。由于接口与 WHATWG WebSocket 兼容,开发者可以继续使用熟悉的事件和状态模型,例如:
- 通过
open事件确认连接建立; - 调用
send()发送文本或二进制消息; - 在
message事件中读取服务端数据; - 监听
error和close,完成错误记录与资源清理。
这对于实时通知、任务进度、设备状态同步和协作编辑等桌面场景尤其有用。共享代码仍需谨慎:主进程与渲染进程的安全边界、代理配置和生命周期并不相同,接口相似不代表运行环境完全一致。
可以这样实践:连接本地 WebSocket 服务
下面是一个可运行的最小项目。示例使用 ws 创建本地回显服务,再通过 Electron 43.2.0 的 net.WebSocket 连接它,因此不依赖公网测试服务。
创建 package.json:
{
"name": "electron-net-websocket-demo",
"version": "1.0.0",
"private": true,
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "43.2.0",
"ws": "^8.18.0"
}
}
创建 main.js:
const { app, net } = require('electron');
const { WebSocketServer } = require('ws');
app.whenReady().then(() => {
const server = new WebSocketServer({ port: 0 });
server.on('connection', (socket) => {
socket.on('message', (data) => {
socket.send(`echo: ${data.toString()}`);
});
});
server.on('listening', () => {
const { port } = server.address();
const socket = new net.WebSocket(`ws://127.0.0.1:${port}`);
socket.addEventListener('open', () => {
console.log('WebSocket connected');
socket.send('hello from Electron');
});
socket.addEventListener('message', (event) => {
console.log('Received:', event.data);
socket.close(1000, 'demo complete');
});
socket.addEventListener('error', (event) => {
console.error('WebSocket error:', event);
});
socket.addEventListener('close', (event) => {
console.log(`Closed: code=${event.code}, reason=${event.reason}`);
server.close(() => app.quit());
});
});
});
安装并运行:
npm install
npm start
正常情况下,终端会依次输出连接成功、回显消息和关闭状态。接入真实服务时,将 ws://127.0.0.1:${port} 替换为自己的 wss:// 地址,并删除示例中的本地 WebSocketServer。
不要把连接管理塞进每个窗口
在多窗口 Electron 应用中,可以让主进程持有唯一 WebSocket 连接,再通过 IPC 向经过授权的渲染进程分发消息。这样能够避免每打开一个窗口就创建一条连接,也便于集中处理认证、重连和日志。
不要直接向页面暴露整个 ipcRenderer 或底层 socket。可以通过 preload 脚本只开放有限的方法:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('realtime', {
send: (message) => ipcRenderer.invoke('realtime:send', message),
onMessage: (callback) => {
const listener = (_event, message) => callback(message);
ipcRenderer.on('realtime:message', listener);
return () => ipcRenderer.removeListener('realtime:message', listener);
}
});
这段代码只是推荐的边界设计示例;主进程仍需校验消息类型、长度和发送方身份。对于包含访问令牌的连接,也应避免将完整 URL、请求头或服务端消息写入普通日志。
升级时检查这些边界
采用 Electron 43.2.0 前,建议完成以下检查:
- 锁定 Electron 版本并在三个目标平台执行冒烟测试;
- 验证企业代理、系统证书以及
wss://握手行为; - 为断网、休眠唤醒和服务端重启设计带退避的重连策略;
- 设置消息大小上限,并验证服务端发送的 JSON 或二进制数据;
- 在窗口关闭和应用退出时清理事件监听器与连接;
- 阅读完整发布说明,确认同一版本中的修复和兼容性变化。
net.WebSocket 的主要价值不是少安装一个依赖,而是让 Electron 主进程采用更标准化的实时通信接口。是否立即迁移,应取决于现有网络库的稳定性、代理需求和测试覆盖;已经成熟运行的连接层没有必要只为统一 API 而仓促重写。