许多桌面应用需要在用户登录系统时自动启动(如通讯工具、下载管理器),或者需要在本地记录用户偏好、窗口位置、授权令牌等信息。Electron 提供了相对简洁的 API 来处理开机自启,而对于配置的读写,则可以利用 Node.js 生态中成熟的轻量级存储方案。本节将从实际项目需求出发,介绍如何在 Windows、macOS 和 Linux 上实现这两类功能。
14.3.1 开机自启动
Electron 的 app 模块内置了设置登录项的能力,主要通过 app.setLoginItemSettings 完成。底层的实现会根据操作系统自动调用相应的注册表项(Windows)、LaunchAgent(macOS)或 autostart 桌面文件(Linux),开发者无需关心平台细节。
基本用法
在你的主进程代码中,调用一次即可将应用注册为登录自启动:
// 主进程
const { app } = require('electron');
// 开启开机自启动
app.setLoginItemSettings({
openAtLogin: true,
path: process.execPath, // 可选,指定可执行文件路径
args: ['--hidden'] // 可选,启动参数,例如最小化到托盘
});
如果需要关闭自启动,只需将 openAtLogin 设为 false。
常用配置项说明:
openAtLogin:true打开自启,false关闭。path:启动哪个可执行文件,默认是当前应用自己的路径。在开发环境中,这个路径可能指向 electron 可执行文件,打包后会自动指向你的应用。args:字符串数组,传递给应用的命令行参数。你可以利用这些参数实现“开机静默启动到托盘”等行为。
检查当前自启动状态
如果需要根据当前自启状态决定 UI 上的开关,可以使用 app.getLoginItemSettings:
const settings = app.getLoginItemSettings();
console.log(settings.openAtLogin); // 是否已注册
console.log(settings.wasOpenedAtLogin); // 如果本次启动是由登录触发,则为 true
wasOpenedAtLogin 对于实现“开机后自动最小化到系统托盘”这类需求特别有用。在应用启动时检测该字段,如果是 true 就不打开主窗口,直接隐藏。
平台差异与注意事项
- Windows:通过
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run注册表项实现。如果应用被卸载但注册表项未被清理,可能会残留无用的启动项(较新的 Electron 版本会自动尝试清理,但建议在卸载程序中显式移除)。 - macOS:使用 Launch Services 添加一个 login item,不会在系统偏好设置的用户登录项中直接显示为可见条目,而是以更底层的方式工作。如果应用经过代码签名和公证,用户可以手动在“系统设置 -> 通用 -> 登录项”进行管理。
- Linux:需要在
~/.config/autostart/目录下创建.desktop文件。Electron 会自动处理这些文件,但要求应用以可执行文件的方式运行(即打包后的 AppImage、deb 等)。部分桌面环境可能需要用户手动启用。
为了更好的用户体验,建议在应用设置页提供一个“开机自启动”的开关,并标注当前状态。切换时将选项持久化到配置文件,并调用 setLoginItemSettings 同步系统设置。
14.3.2 配置写入与本地存储
桌面应用通常需要保存配置数据,例如窗口大小和位置、用户偏好语言、最近打开的文件列表、API 密钥等。这些数据通常存储在用户的本地文件系统中。Electron 环境下有多种方案可选:
- 使用
electron-store:最流行的键值存储库,自动将数据保存为 JSON 文件,支持加密和模式验证。 - 直接使用
fs写入 JSON 文件:灵活简单,适合小规模数据。 - 轻量级数据库:如
better-sqlite3(适用于大量结构化数据),不在本节讨论范围内。
electron-store 的典型用法
安装:
npm install electron-store
在主进程中使用(由于涉及文件读写,建议只在主进程操作,通过 IPC 暴露给渲染进程):
// 主进程 store.js
const Store = require('electron-store');
const store = new Store({
// 可选:对敏感数据加密
encryptionKey: 'my-secret-key',
// 可选:定义默认值
defaults: {
windowBounds: { width: 1024, height: 768 },
theme: 'system'
}
});
// 读写方法
store.set('theme', 'dark');
const theme = store.get('theme'); // 'dark'
store.delete('tempData');
// 导出 IPC 处理器
ipcMain.handle('store-get', (event, key) => store.get(key));
ipcMain.handle('store-set', (event, key, value) => {
store.set(key, value);
return true;
});
在渲染进程中通过 IPC 调用:
// 预加载脚本暴露安全接口
contextBridge.exposeInMainWorld('storeAPI', {
get: (key) => ipcRenderer.invoke('store-get', key),
set: (key, value) => ipcRenderer.invoke('store-set', key, value)
});
// 渲染进程中使用
document.querySelector('#theme-switch').addEventListener('change', async (e) => {
await window.storeAPI.set('theme', e.target.checked ? 'dark' : 'light');
});
electron-store 会自动将数据存放在 app.getPath('userData') 目录下的 config.json 文件中,不同操作系统对应路径为:
- Windows:
C:\Users\<用户名>\AppData\Roaming\<应用名>\config.json - macOS:
~/Library/Application Support/<应用名>/config.json - Linux:
~/.config/<应用名>/config.json
使用原生文件写入
如果你不需要 electron-store 的附加功能,也可以直接读写 JSON 文件:
const fs = require('fs');
const path = require('path');
const { app } = require('electron');
const configPath = path.join(app.getPath('userData'), 'preferences.json');
function readConfig() {
try {
const data = fs.readFileSync(configPath, 'utf-8');
return JSON.parse(data);
} catch (error) {
return {}; // 文件不存在则返回空对象
}
}
function writeConfig(config) {
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
}
这种方式足够简单,但需要注意文件可能损坏、读写权限等问题。建议用 try-catch 包裹并做好容错。
写入 Windows 注册表(仅 Windows)
针对某些需要深度集成 Windows 系统的场景(如修改系统级设置、文件关联等),你可能需要直接操作注册表。Electron 没有内建注册表 API,但可以通过在 Node.js 环境下执行命令行(reg 命令)或者使用原生模块(如 node-winreg)来实现。
使用 child_process 执行 reg 命令示例(添加一个开机自启之外的注册表项):
const { exec } = require('child_process');
// 在 HKCU 下写入一个字符串值
exec('reg add HKCU\\Software\\MyApp /v InstallPath /t REG_SZ /d "C:\\Program Files\\MyApp" /f',
(error, stdout, stderr) => {
if (error) {
console.error(`执行错误: ${error}`);
return;
}
console.log('注册表写入成功');
}
);
更推荐的方式是使用 node-winreg 库,因为它提供了基于 promise 的接口,并且自动处理字符串转义:
npm install winreg
const Winreg = require('winreg');
const regKey = new Winreg({
hive: Winreg.HKCU,
key: '\\Software\\MyApp'
});
regKey.set('InstallPath', Winreg.REG_SZ, 'C:\\Program Files\\MyApp', (err) => {
if (err) console.error(err);
else console.log('注册表写入成功');
});
注意:注册表操作需要应用具有足够的权限(通常以当前用户身份运行即可修改 HKCU),且卸载应用时应该清理相关键值,避免残留。
14.3.3 实际开发建议
- 开机自启的实现:优先使用 Electron 原生 API,不要自行操作注册表或 plist,以避免平台兼容性问题和签名失效。
- 配置存储的选型:对于简单的键值对,
electron-store是生产力最高的选择;如果配置结构复杂且需要频繁同步到 UI,考虑结合 Vuex/Pinia 等状态管理库,并监听变化自动保存。 - 安全性:任何存储在本地的数据都应当被认为是可被用户读取的。敏感信息(如密码、token)务必加密存储。
electron-store提供了内置的加密选项,也可以使用safeStorageAPI 进行加解密。 - 文件位置:始终将用户配置写到
app.getPath('userData')目录下,而不是应用安装目录,这样符合主流系统的规范,并避免权限问题。 - 恢复默认:提供“重置所有设置”的功能,它只需要删除对应的配置文件或清空 store,重新启动应用即可。
通过合理利用这些内置能力和社区方案,你可以在保持代码简洁的前提下,赋予应用“真正桌面软件”所具备的用户体验,让用户感到它完全融入了操作系统的运行节奏。