主题切换
附录 C:多平台差异速查表
Electron 的"一次编写,到处运行"并非魔法,不同操作系统在路径处理、通知行为、窗口管理和代码签名等方面存在显著差异。本附录汇总了开发中最常遇到的平台差异,助你提前避坑。
C.1 路径与环境差异
| 场景 | Windows | macOS | Linux |
|---|---|---|---|
| 路径分隔符 | \(反斜杠) | / | / |
| 换行符 | \r\n | \n | \n |
| 用户数据目录 | %APPDATA% | ~/Library/Application Support | ~/.config |
| 临时目录 | %TEMP% | /tmp 或 $TMPDIR | /tmp |
| 可执行文件后缀 | .exe | 无 | 无 |
| 隐藏文件前缀 | 属性标记 | .(点号) | .(点号) |
javascript
// 跨平台路径处理(始终使用 path 模块)
const path = require('path')
// 正确
const filePath = path.join(app.getPath('userData'), 'config.json')
// 错误(硬编码分隔符)
const badPath = `${app.getPath('userData')}/config.json`C.2 通知系统差异
| 特性 | Windows | macOS | Linux |
|---|---|---|---|
| 通知中心 | 操作中心 | 通知中心 | 依赖桌面环境 |
| 图标尺寸 | 系统自适应 | 必须 256x256 | 系统自适应 |
| 声音 | 支持自定义 | 系统默认 | 有限支持 |
| 持久化 | 保留在历史中 | 保留在历史中 | 通常不保留 |
| 点击回调 | 支持 | 支持 | 部分支持 |
Linux 通知注意事项
- Ubuntu 使用
notify-osd,GNOME 使用原生通知 - 某些 Linux 发行版需要安装
libnotify依赖 - 通知图标可能无法正常显示,建议提供绝对路径
C.3 托盘与菜单差异
| 特性 | Windows | macOS | Linux |
|---|---|---|---|
| 托盘图标 | 任务栏右下角 | 菜单栏右上角 | 系统托盘区 |
| 图标格式 | .ico 推荐 | .png / .icns | .png |
| 图标尺寸 | 16x16 | 16x16 (Retina 32x32) | 22x22 |
| 单击行为 | 打开菜单 | 打开菜单 | 打开菜单 |
| 双击行为 | 唤起应用 | 无效果 | 唤起应用 |
| 右键行为 | 打开菜单 | 打开菜单 | 打开菜单 |
javascript
// 跨平台托盘图标加载
const getTrayIcon = () => {
if (process.platform === 'win32') {
return path.join(__dirname, 'assets/tray-icon.ico')
}
if (process.platform === 'darwin') {
return path.join(__dirname, 'assets/tray-iconTemplate.png') // Template 图标支持暗黑模式
}
return path.join(__dirname, 'assets/tray-icon.png')
}C.4 窗口行为差异
| 特性 | Windows | macOS | Linux |
|---|---|---|---|
| 关闭按钮 | 完全退出(默认) | 隐藏到 Dock(默认) | 完全退出 |
| 最小化 | 任务栏 | Dock | 任务栏/系统托盘 |
| 无边框窗口 | 支持 | 支持(需 titleBarStyle) | 支持 |
| 透明窗口 | 支持 | 支持 | 有限支持 |
| 窗口置顶 | alwaysOnTop | alwaysOnTop + visibleOnFullScreen | alwaysOnTop |
| 全屏按钮 | 无系统按钮 | 左上角绿色按钮 | 依赖桌面环境 |
javascript
// macOS 关闭按钮默认行为:隐藏而非退出
app.on('window-all-closed', () => {
// macOS 上保持应用运行
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// macOS 点击 Dock 图标时重新创建窗口
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})C.5 对话框差异
| 特性 | Windows | macOS | Linux |
|---|---|---|---|
| 文件选择器 | 原生资源管理器 | Finder | GTK/QT 文件选择器 |
| 默认路径 | 支持 | 支持 | 支持 |
| 过滤器 | 支持 | 支持 | 支持 |
| 多选 | 支持 | 支持 | 支持 |
| 保存确认覆盖 | 自动提示 | 自动提示 | 自动提示 |
对话框最佳实践
- 始终在对话框选项中提供
defaultPath,提升用户体验 - macOS 的
openDirectory和createDirectory行为与 Windows 略有不同 - Linux 下某些桌面环境可能不支持
messageBox的所有图标类型
C.6 代码签名差异
| 项目 | Windows | macOS | Linux |
|---|---|---|---|
| 签名必要性 | 推荐(SmartScreen) | 必须(Gatekeeper) | 可选 |
| 证书类型 | EV/OV 代码签名证书 | Apple Developer ID | GPG |
| 证书费用 | $200-700/年 | $99/年(Apple Developer) | 免费 |
| 公证(Notarization) | 不需要 | 强烈推荐 | 不需要 |
| 签名工具 | signtool / osslsigncode | codesign + xcrun altool | gpg |
| 警告提示 | "未知发布者" | "无法打开" | 无 |
Windows 签名命令
bash
# 使用 signtool(Windows SDK)
signtool sign /f certificate.pfx /p password /tr http://timestamp.digicert.com /td sha256 /fd sha256 MyApp.exe
# 使用 osslsigncode(跨平台)
osslsigncode sign -certs cert.pem -key key.pem -n "MyApp" -i https://myapp.com -in MyApp.exe -out MyApp-signed.exemacOS 签名与公证
bash
# 签名应用
codesign --deep --force --verify --verbose --sign "Developer ID Application: Your Name" MyApp.app
# 公证
xcrun altool --notarize-app --primary-bundle-id "com.myapp" --username "your@email.com" --password "@keychain:AC_PASSWORD" --file MyApp.zip
# stapl 公证结果
xcrun stapler staple MyApp.appC.7 打包配置差异速查
yaml
# electron-builder.yml 跨平台配置
productName: MyApp
appId: com.myapp.desktop
directories:
output: dist
files:
- "**/*"
- "!**/*.map"
# Windows 专属
win:
target:
- target: nsis
arch: [x64, ia32]
publisherName: "Your Company"
verifyUpdateCodeSignature: false
# macOS 专属
mac:
target: [dmg, zip]
category: public.app-category.productivity
hardenedRuntime: true
gatekeeperAssess: false
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
# Linux 专属
linux:
target: [AppImage, deb, rpm]
category: Utility
maintainer: "Your Name"
vendor: "Your Company"
synopsis: "My Electron App"
description: "A cross-platform desktop application"平台检测代码
javascript
const isWindows = process.platform === 'win32'
const isMac = process.platform === 'darwin'
const isLinux = process.platform === 'linux'
// 或在一个地方统一导出
module.exports = { isWindows, isMac, isLinux }C.8 键盘快捷键差异
不同平台的修饰键映射是跨平台开发中最常见的差异之一:
| 操作 | Windows/Linux | macOS |
|---|---|---|
| 复制 | Ctrl+C | Cmd+C |
| 粘贴 | Ctrl+V | Cmd+V |
| 保存 | Ctrl+S | Cmd+S |
| 撤销 | Ctrl+Z | Cmd+Z |
| 全选 | Ctrl+A | Cmd+A |
| 开发者工具 | F12 或 Ctrl+Shift+I | Cmd+Option+I |
javascript
// 跨平台快捷键处理
const isMac = process.platform === 'darwin'
const accelerators = {
save: isMac ? 'Cmd+S' : 'Ctrl+S',
undo: isMac ? 'Cmd+Z' : 'Ctrl+Z',
// 使用 CmdOrCtrl 自动适配
find: 'CmdOrCtrl+F',
devTools: 'CmdOrCtrl+Shift+I'
}C.9 文件系统差异
| 特性 | Windows | macOS | Linux |
|---|---|---|---|
| 大小写敏感 | 不敏感(NTFS 可配置) | 默认不敏感(APFS 可配置) | 敏感 |
| 路径长度限制 | 260 字符(MAX_PATH) | 1024 字符 | 4096 字符 |
| 文件名禁用字符 | \ / : * ? " < > | | / 和 NULL | / 和 NULL |
| 隐藏文件 | 文件属性标记 | . 前缀 | . 前缀 |
javascript
// 长路径处理(Windows)
// electron-builder 中配置长路径支持
// 或在应用启动时注册
if (process.platform === 'win32') {
// Node.js 默认支持长路径,但某些 Windows API 需要注册表配置
app.setAsDefaultProtocolClient('myapp')
}大小写敏感陷阱
在 Windows 上开发时,require('./Config.json') 和 require('./config.json') 都能正常工作。但部署到 Linux 后,后者会找不到文件。始终使用一致的、与文件系统完全匹配的大小写命名。
C.10 电源管理与休眠检测
powerMonitor API 在各平台上的行为差异会影响长时间运行的自动化任务:
| 事件 | Windows | macOS | Linux |
|---|---|---|---|
suspend | 系统休眠/睡眠时触发 | 系统睡眠时触发 | 依赖桌面环境 |
resume | 系统唤醒时触发 | 系统唤醒时触发 | 依赖桌面环境 |
on-ac | 接通电源时触发 | 接通电源时触发 | 部分支持 |
on-battery | 断开电源时触发 | 断开电源时触发 | 部分支持 |
shutdown | 系统关机前触发 | 不支持 | 不支持 |
javascript
const { powerMonitor } = require('electron')
// 休眠前暂停自动化任务
powerMonitor.on('suspend', () => {
console.log('系统即将休眠,暂停自动化任务')
// 保存当前状态到磁盘
automationEngine.pause()
automationEngine.saveState()
})
// 唤醒后恢复任务
powerMonitor.on('resume', () => {
console.log('系统已唤醒,恢复自动化任务')
automationEngine.restoreState()
automationEngine.resume()
})RPA 场景建议
长时间运行的自动化任务应监听 powerMonitor,在系统休眠前保存执行状态,避免任务中断导致数据丢失。