Skip to content

桌面应用:Electron / Tauri

桌面客户端的现代主流是「Web 技术做界面 + 壳负责系统能力」:Electron 自带 Chromium,Tauri 用系统 WebView。本页先给方案全景,再分别讲两条路线的模型、安全基线与分发,避免「界面写得爽、壳上踩大坑」。

桌面方案全景

方案界面内核/运行体积与内存适合
ElectronWeb(Chromium)Node + Chromium成熟产品、需要 Node 生态(VS Code、Slack 系)
TauriWeb系统 WebView + Rust轻量工具、团队会 Rust、重安全
Flutter DesktopFlutter自绘引擎想与移动端共用一套 Flutter 代码
Qt / 原生Qt / 系统 API原生重系统集成、传统桌面
WailsWeb系统 WebView + GoGo 团队轻量方案

选型已放在技术全景与选型,本页聚焦实现层。

Electron:主进程与渲染进程

进程模型是一切认知的基础:

进程职责能力边界
主进程(Node)窗口管理、系统能力、生命周期可访问 Node 与系统 API
渲染进程页面 UI(Chromium)默认被沙箱限制,不直接拥有 Node 全量能力
preload 脚本桥接主/渲染在渲染进程加载前执行,暴露受控 API

安全基线(顺序重要,见「安全红线」节):

  • contextIsolation: true(渲染进程与 preload 隔离);
  • nodeIntegration: false(渲染进程不开 Node);
  • sandbox: true
  • contextBridge.exposeInMainWorld 只暴露白名单 API;
  • IPC 用 ipcRenderer.invoke + ipcMain.handle(异步请求应答模式),少用 send/on 全局事件。
ts
// preload.ts —— 只暴露两个最小能力
import { contextBridge, ipcRenderer } from 'electron'

contextBridge.exposeInMainWorld('app', {
  getVersion: () => ipcRenderer.invoke('app:get-version'),
  openExternal: (url: string) => ipcRenderer.invoke('app:open-external', url)
})
ts
// main.ts
import { app, BrowserWindow, ipcMain, shell } from 'electron'

app.whenReady().then(() => {
  const win = new BrowserWindow({
    width: 1024, height: 720,
    webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true, preload: PRELOAD_PATH }
  })
})

ipcMain.handle('app:get-version', () => app.getVersion())
ipcMain.handle('app:open-external', (_e, url: string) => shell.openExternal(url))

渲染进程安全红线

红线说明
绝不开 nodeIntegration渲染页面一旦有 XSS,等于直接 RCE
preload 只暴露最小面不要 exposeInMainWorld('api', ipcRenderer) 整对象
渲染进程不加载远程 JS主窗口只加载本地文件或受控远程内容
CSP 生效给 HTML 加 CSP,限制 script-src
校验 IPC 参数handle 里对来自渲染的参数做白名单/类型校验(渲染可能被攻破)
外链必须走白名单openExternal 只允许协议白名单

这些纪律在 Electron 里不是「最佳实践」而是「安全下限」——渲染进程是不可信输入。

Electron 常用能力

  • 单实例锁:app.requestSingleInstanceLock(),防止开多个实例;
  • 托盘 Tray、系统菜单 Menu、全局快捷键 globalShortcut
  • 设置持久化:electron-store;系统对话框 dialog;文件访问 fs/协议;
  • 自动更新:electron-updater(配合打包产物与更新服务,macOS 需签名);
  • 窗口事件:ready-to-show 再显示避免白屏闪烁。

Tauri 2:Rust 内核 + 系统 WebView

Tauri 2(2.x 稳定线)理念:能 Web 解决的归 Web,要系统能力的归 Rust

  • 界面:前端(任意框架,本地打包或远端可配)渲染到系统 WebView;
  • 逻辑:#[tauri::command] 暴露给前端调用;编译产物是原生二进制,无 Node 运行时;
  • 权限模型capabilities(ACL)——前端能调哪些命令、读哪些路径,要在 tauri.conf.json 与 capabilities 文件显式声明,未授权调用直接拒绝;
  • 插件体系:官方插件(store、dialog、fs、updater、notification)与社区插件都走同一授权模型;
  • 配置:tauri.conf.json 管理窗口、构建、打包、bundle 标识;
  • 多窗口与事件:WebviewWindowemit/listen 事件总线。
rust
// main.rs 侧暴露一个命令
#[tauri::command]
fn greet(name: String) -> String {
    format!("Hello, {}!", name)
}

// 注册命令
tauri::Builder::default()
    .invoke_handler(tauri::generate_handler![greet])
    .run(tauri::generate_context!())
    .expect("error while running tauri application");

前端(若用 JS 侧 SDK):

js
import { invoke } from '@tauri-apps/api/core'

const msg = await invoke('greet', { name: 'Rust' })

安全注意:即使前端被 XSS 攻破,能力也被 capabilities 白名单兜底——这正是 Tauri 把安全做进框架的方式。

Tauri 常用能力

  • 系统 WebView 无 Chromium 体积负担:安装包典型个位数到十几 MB;
  • updater 更新需公钥签名(Tauri 默认要求 updater 签名),CI 里保管私钥;
  • 跨平台构建注意:macOS 打包要在 macOS 上(签名/公证),Windows 亦然,CI 用对应 runner;
  • 与 Rust 生态互操作强(文件处理、本地服务、加密等都可走 Rust crate)。

打包、签名与自动更新

两条路线共用的底座:

  1. 代码签名是桌面发布的前提:macOS 需 Developer ID 签名 + notarization(公证),否则 Gatekeeper 拦下载;Windows 需代码签名证书避免 SmartScreen 惊吓;Linux 走 deb/rpm/AppImage 与 Flatpak/AppArmor 语境;
  2. 自动更新与签名强绑定:Electron 用 electron-updater;Tauri 用官方 updater(签名公钥放前端配置);更新失败要有回退(启动自检 + 下载失败提示);
  3. 安装包与商店:macOS App Store、Microsoft Store 有各自沙箱与审核约束,与官网直发路线并存;
  4. CI 里构建矩阵按系统跑,签名私钥/公证凭据放 secret 仓库(详见打包、签名与发布容器与部署的 CI 一节)。

常见坑速查

现象对策
Electron 开着 nodeIntegration渲染页一点 XSS 就整机 RCE默认全关 + contextIsolation + preload 白名单
preload 把 ipcRenderer 整包暴露渲染进程可发任意命令只 expose 封装好的最小方法
IPC 参数不校验主进程被伪造请求打穿handle 里白名单校验,别信任渲染进程
窗口白屏闪烁冷启动视觉差ready-to-show 后再 show;用背景色兜底
多开用户拖出多个主窗口/托盘混乱单实例锁 + 二次激活聚焦主窗口
Tauri 未授权调用前端报权限拒绝却不知为何在 capabilities 文件补 scope/命令声明
更新包未签名updater 校验失败或系统拦截Tauri 配签名;Electron 关 macOS 弹窗需签
忘公证macOS 用户「已损坏,打不开」notarization + staple,CI 脚本化
升级把用户配置弄丢大版本 schema 变了崩溃迁移版本化:settings schema 带版本号
只在本机测不同系统表现差异(字体/DPI/路径)CI 三系统构建 + 冒烟清单

检查清单

  • [ ] 已选定主从/前后端架构与权限模型,安全基线全开;
  • [ ] 渲染进程不可信原则贯穿(preload 白名单 + IPC 校验 + CSP);
  • [ ] 自动更新带签名与失败回退;
  • [ ] 三系统(Win/macOS/Linux)构建与冒烟清单齐备;
  • [ ] 配置持久化带版本迁移,升级不丢用户数据;
  • [ ] 单实例、托盘、深链等桌面习惯做了。

相关链接:技术全景与选型 · 打包、签名与发布 · 性能与稳定性 · 写作规范见客户端开发索引

基于 VitePress 构建 · 内容以知识共享方式沉淀