Electron 应用的生命周期由 app 模块的事件系统精确控制。理解这些事件的发生顺序和适用场景,是编写稳定、优雅桌面程序的基础。它们覆盖了从应用启动到彻底退出的每一个关键节点,并在不同操作系统上有细节差异。
1. 启动:ready 事件 —— 一切开始的地方
const { app, BrowserWindow } = require('electron');
app.whenReady().then(() => {
// 安全创建窗口、注册协议、初始化系统托盘等
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
});
ready 是 Electron 应用启动后的第一个核心事件,它在以下条件同时满足时触发:
- Electron 完成内部初始化(Chromium 和 Node.js 环境就绪)。
- 平台相关的就绪状态完成(例如 macOS 上等待
NSApplication的applicationDidFinishLaunching通知)。
实用要点:
所有涉及窗口创建、系统菜单设置、全局快捷键注册、自动更新检查等初始化操作,都应该在 ready 之后执行。推荐使用 app.whenReady() 返回的 Promise 代替监听 app.on('ready', ...),它可以自动处理 ready 事件已经触发过的情况,避免事件竞争问题。
2. 窗口全部关闭:window-all-closed 事件
app.on('window-all-closed', () => {
// 在 macOS 上,通常不退出应用,保留进程在 Dock 栏
if (process.platform !== 'darwin') {
app.quit();
}
});
当所有 BrowserWindow 关闭后,Electron 会触发 window-all-closed 事件。这是决定应用是否退出的重要分界点:
- Windows 和 Linux:惯例是窗口全关后退出应用,因此通常在这里调用
app.quit()。 - macOS:用户期望关闭所有窗口后应用依然留在程序坞(Dock)中,可以再次激活。因此这里一般不做退出操作,而依赖
activate事件来重建窗口。
如果不处理这个事件,默认行为是应用不会自动退出,仍然保持主进程运行。
3. 退出流程:before-quit、will-quit、quit
应用退出是一个多阶段过程,每一步都提供了“最后十分钟”的干预机会:
app.on('before-quit', (event) => {
// 可以阻止退出(仅在用户主动退出时有效)
// event.preventDefault(); // 取消退出,一般用于未保存提示
});
app.on('will-quit', (event) => {
// 退出无法阻止,但可以做最后的清理工作
// 关闭数据库连接、保存状态、删除临时文件等
});
app.on('quit', (event, exitCode) => {
// 应用已经退出(进程即将结束),回调内不要再做异步操作
});
before-quit:在窗口关闭之前触发,由用户操作(如点击关闭按钮、按下 <kbd>Cmd+Q</kbd>)或程序调用app.quit()引发。可以通过event.preventDefault()阻止退出,常用于实现“文档未保存”的二次确认弹窗。如果退出由程序自动触发(如app.quit()),有时此事件的行为会因平台略有不同。will-quit:此时所有窗口已经关闭,开始卸载资源。该事件不能再阻止退出,适合执行同步或可控异步的收尾工作,例如写入最后的状态文件、断开数据库连接。注意,如果应用因崩溃或被SIGTERM信号杀死,此事件可能不会被触发。quit:进程即将退出前的最后一刻,只用于做一些同步的最终操作(如记录退出日志)。不建议在这里初始化异步任务,因为进程可能随时终止。
4. 激活:activate 事件(macOS 特色)
app.on('activate', () => {
// 在 macOS 上,当用户点击 Dock 图标且没有窗口时触发
if (BrowserWindow.getAllWindows().length === 0) {
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile('index.html');
}
});
当用户在 macOS 上点击程序坞图标、或在 Launchpad 中激活应用时,如果此时没有可用窗口,Electron 会触发 activate 事件。结合 window-all-closed 不退出应用的逻辑,这个事件成为重建窗口的最佳时机,完美复现 macOS 原生应用的行为习惯。
在 Windows 和 Linux 上,也存在类似行为(例如从任务栏点击图标恢复窗口),但 Electron 会在窗口最小化或隐藏时自动处理,通常不需要额外监听。
5. 系统休眠与唤醒:powerMonitor 模块
虽然不属于 app 模块直接事件,但应用对系统休眠(挂起)和唤醒的响应同样构成生命周期管理的重要一环:
const { powerMonitor } = require('electron');
powerMonitor.on('suspend', () => {
// 系统即将进入休眠或睡眠状态
// 可以暂停网络请求、停止计时器、保存关键状态
});
powerMonitor.on('resume', () => {
// 系统从休眠中唤醒
// 可以重新连接 WebSocket、刷新 UI 数据
});
suspend:当操作系统进入挂起模式(如笔记本合盖)时触发。此时应用应尽快冻结非必要活动,释放独占资源,并做好状态持久化,因为进程可能被系统强制暂停。resume:恢复后触发。应用可以在这里检查网络连接、重新获取实时数据、恢复之前暂停的后台任务。
此外,powerMonitor 还能提供锁定屏幕(lock-screen)和解锁屏幕(unlock-screen)的事件,在对安全性敏感的应用中可以用于隐藏隐私内容或要求重新认证。
真实项目中的最佳实践
- 状态保留与恢复:在
before-quit或will-quit中,将窗口大小、位置、打开的文件列表等状态写入app.getPath('userData')下的本地文件;在ready或窗口创建时读回。这能实现应用“从哪里退出,就从哪里打开”的体验。 - 安全退出保护:对于编辑器、设计工具等,在
mainWindow.on('close')事件中检查未保存内容,如果存在则阻止关闭并弹出对话框,而不是直接在before-quit中拦截,这样逻辑更清晰。 - 多窗口管理:使用一个计数器或窗口集合来跟踪活跃窗口,在
window-all-closed中根据平台决定是否退出;在activate中仅当计数器为零时才新建窗口,避免创建多余窗口。 - 休眠时的资源释放:如果应用使用了摄像头、音频输入或长时间轮询,务必在
suspend中停止这些操作,否则可能在唤醒后引发硬件冲突或过高的 CPU 占用。
掌握这些生命周期事件,意味着你能让应用表现得像一个真正的“桌面公民”——优雅地启动、得体地退出,并在系统休眠、激活等边界情境下保持体验连贯。结合主渲染进程分离的架构,这些逻辑应该全部写在主进程中,并使界面层通过 IPC 同步关键状态。