23、Electron桌面应用:Electron集成、系统托盘、窗口管理、硬件加速

说实话,WebRTC 做视频会议,大部分时间都在折腾浏览器。但真到了产品化阶段,你会发现——用户要的是一个能双击打开的桌面应用,而不是一个需要输入网址的页面。这时候,Electron 就派上用场了。

我个人习惯把 Electron 看作「浏览器 + Node.js 的合体」。它用 Chromium 渲染界面,用 Node.js 跑本地逻辑。说白了,你写的 WebRTC 代码几乎不用改,就能直接跑在桌面上。但这里有几个坑,我今天跟你聊聊。

Electron 集成:从零到一

先搭个最简单的架子。你肯定已经装好了 Node.js,那咱们直接开干:

mkdir webrtc-desktop
cd webrtc-desktop
npm init -y
npm install electron --save-dev

然后创建一个 main.js,这是 Electron 的主进程入口:

const { app, BrowserWindow } = require('electron')

function createWindow () {
  const win = new BrowserWindow({
    width: 1280,
    height: 720,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  })
  win.loadURL('http://localhost:3000') // 你的 WebRTC 前端地址
}

app.whenReady().then(createWindow)

嗯,这里要注意。我早期做项目时直接把 nodeIntegration 设为 true,结果发现渲染进程能直接调 require('child_process'),这太危险了。生产环境建议用 contextBridge 暴露有限的 API。

系统托盘:让应用常驻后台

视频会议应用最怕什么?用户不小心关掉了窗口,然后会议断了。系统托盘就是解决方案——窗口可以关,但进程还在,用户随时能点回来。

const { Tray, Menu } = require('electron')

let tray = null

app.whenReady().then(() => {
  tray = new Tray('icon.png')
  const contextMenu = Menu.buildFromTemplate([
    { label: '显示窗口', click: () => mainWindow.show() },
    { label: '静音切换', type: 'checkbox', checked: false },
    { type: 'separator' },
    { label: '退出', click: () => app.quit() }
  ])
  tray.setToolTip('WebRTC 视频会议')
  tray.setContextMenu(contextMenu)
})

我曾经踩过一个坑:托盘图标在 macOS 上必须是 22x22 的 @2x 图,否则糊得一塌糊涂。Windows 上倒是随意些,但建议准备 16x16 和 32x32 两套。

窗口管理:别让用户手忙脚乱

多人视频会议里,窗口行为很关键。用户可能同时开着聊天、共享屏幕、参会者列表。如果每个都弹一个新窗口,桌面就乱套了。

我建议用单窗口 + 多视图模式。Electron 的 BrowserView 可以嵌入子视图:

const { BrowserView } = require('electron')

const view = new BrowserView()
mainWindow.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 300, height: 600 })
view.webContents.loadURL('https://chat.example.com')

这样所有功能都在一个窗口里,用户只需要拖动分割线调整布局。你想想看,是不是比满屏弹窗舒服多了?

另外,窗口状态要记住。用户调整了大小、位置,下次打开应该恢复原样:

const settings = require('electron-settings')

mainWindow.on('close', () => {
  settings.set('windowBounds', mainWindow.getBounds())
})

app.on('ready', () => {
  const bounds = settings.get('windowBounds')
  if (bounds) mainWindow.setBounds(bounds)
})

硬件加速:性能的隐形杀手

WebRTC 本身就很吃资源,编码解码、渲染视频流,全靠 GPU 撑着。Electron 默认开启了硬件加速,但有时候反而会出问题。

为什么会这样?因为 Electron 的 Chromium 版本和你的显卡驱动可能不兼容。我遇到过几次:用户反馈视频卡顿,一查发现是硬件加速导致渲染崩溃,回退到软件渲染反而流畅了。

你可以这样控制:

app.disableHardwareAcceleration() // 全局关闭

// 或者针对特定窗口
const win = new BrowserWindow({
  webPreferences: {
    enableWebGL: false,
    enableHardwareAcceleration: false
  }
})

但别一刀切。我建议在启动时检测 GPU 信息:

const gpuInfo = app.getGPUFeatureStatus()
if (gpuInfo.webgl === 'disabled') {
  console.warn('WebGL 不可用,关闭硬件加速')
  app.disableHardwareAcceleration()
}

说白了,硬件加速是个好东西,但别迷信它。遇到诡异问题,先关了试试。

知识体系总览

下面这张图帮你理清本章的核心逻辑:

Electron 桌面应用 Electron 集成 系统托盘 窗口管理 硬件加速 main.js 入口 contextBridge BrowserView 窗口状态持久化 GPU 检测

核心要点:Electron 集成的本质是「Web 页面 + 本地能力」。系统托盘保证应用不死,窗口管理让交互不乱,硬件加速决定性能上限。这三者配合好了,你的 WebRTC 桌面应用才算真正能用。

小技巧:调试 Electron 时,可以用 --inspect 参数启动,然后在 Chrome 浏览器里打开 chrome://inspect,就能像调试网页一样调试 Electron 的主进程。我几乎天天用这个。

注意:Electron 打包后的应用体积不小,一个空壳子就 100MB+。如果用户机器配置低,启动速度会慢。建议用 electron-builder 的 asar 压缩,并且按平台分包,别一股脑全塞进去。

好了,这一章就聊到这儿。桌面应用开发是个细致活,尤其是跟 WebRTC 这种实时通信结合,每一步都要稳。你动手试试,有问题随时翻回来看看。


公众号:蓝海资料掘金营,微信deep3321