# ElectronEgg 生命周期详解 本文档详细说明 ElectronEgg 框架的应用生命周期机制及其在项目中的实现。 ## 生命周期流程图 ``` ┌─────────────┐ │ new │ 创建 ElectronEgg 实例 └──────┬──────┘ │ ┌──────▼──────┐ │ ready │ core app 加载完成(ee-core 框架初始化) └──────┬──────┘ │ ┌──────▼─────────────┐ │ electronAppReady │ Electron app 加载完成 └──────┬─────────────┘ │ ├─────────────────┐ │ │ ┌──────▼──────┐ ┌─────▼────────┐ │ mainWindow │ │ windowReady │ 主窗口创建完成 └──────┬──────┘ └─────▲────────┘ │ │ └─────────────────┘ │ ┌──────▼──────┐ │ running │ 应用运行中 └──────┬──────┘ │ ┌──────▼──────┐ │beforeClose │ 退出之前触发 └──────┬──────┘ │ ┌──────▼──────┐ │ quit │ 应用退出 └─────────────┘ ``` ## 生命周期钩子详解 ### 1. new - 实例创建 **触发时机**:调用 `new ElectronEgg()` 时 **实现位置**:[electron/main.js](electron/main.js#L6) ```javascript const { ElectronEgg } = require('ee-core'); const app = new ElectronEgg(); ``` **作用**: - 创建 ElectronEgg 应用实例 - 初始化框架核心模块 - 准备生命周期管理器 --- ### 2. ready - 核心应用就绪 **触发时机**:ee-core 框架加载完成,Electron app 启动之前 **实现位置**: - 注册:[electron/main.js](electron/main.js#L10) - 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L12-L14) ```javascript // 注册 app.register("ready", life.ready); // 实现 async ready() { logger.info('[lifecycle] ready'); // 在这里可以做: // - 初始化数据库连接 // - 加载配置文件 // - 初始化全局变量 } ``` **适用场景**: - ✅ 初始化数据库连接 - ✅ 加载应用配置 - ✅ 注册全局服务 - ✅ 初始化日志系统 - ❌ 不能操作窗口(窗口还未创建) --- ### 3. electronAppReady - Electron 应用就绪 **触发时机**:Electron 的 `app.ready` 事件触发后,主窗口创建之前 **实现位置**: - 注册:[electron/main.js](electron/main.js#L11) - 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L19-L21) ```javascript // 注册 app.register("electron-app-ready", life.electronAppReady); // 实现 async electronAppReady() { logger.info('[lifecycle] electron-app-ready'); // 在这里可以做: // - 注册全局快捷键 // - 设置应用菜单 // - 初始化托盘图标 // - 注册协议处理 } ``` **适用场景**: - ✅ 注册全局快捷键 (globalShortcut) - ✅ 创建应用菜单 (Menu) - ✅ 创建系统托盘 (Tray) - ✅ 注册自定义协议 (protocol) - ⚠️ 可以创建窗口,但通常在框架内部自动创建 --- ### 4. mainWindow - 主窗口创建 **触发时机**:框架创建主窗口时(内部流程,不需要手动注册) **说明**: - 这是框架内部自动执行的步骤 - 根据 `electron/config/config.*.js` 中的 `windowsOption` 配置创建窗口 - 窗口创建完成后会触发 `windowReady` 钩子 **配置示例**: ```javascript // electron/config/config.default.js windowsOption: { width: 1200, height: 800, show: false, // 设置为 false 可实现无白屏启动 webPreferences: { contextIsolation: false, nodeIntegration: true } } ``` --- ### 5. windowReady - 窗口就绪 **触发时机**:主窗口创建完成,页面加载完毕 **实现位置**: - 注册:[electron/main.js](electron/main.js#L12) - 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L26-L37) ```javascript // 注册 app.register("window-ready", life.windowReady); // 实现 async windowReady() { logger.info('[lifecycle] window-ready'); // 延迟显示窗口,避免白屏 const { windowsOption } = getConfig(); if (windowsOption.show == false) { const win = getMainWindow(); win.once('ready-to-show', () => { win.show(); // 显示窗口 win.focus(); // 聚焦窗口 }) } // 在这里可以做: // - 向渲染进程发送初始化数据 // - 检查更新 // - 加载用户配置 } ``` **适用场景**: - ✅ 操作主窗口 (show/hide/maximize 等) - ✅ 向渲染进程发送消息 - ✅ 执行自动更新检查 - ✅ 加载用户数据并同步到前端 - ✅ 实现无白屏启动(配合 `show: false`) --- ### 6. running - 应用运行中 **触发时机**:窗口显示后,应用正常运行期间 **说明**: - 这不是一个独立的生命周期钩子 - 表示应用的正常运行状态 - 此时所有功能都可用 **可用操作**: - IPC 通信(前后端交互) - 业务逻辑处理 - 数据库操作 - 网络请求 - 文件系统操作 --- ### 7. beforeClose - 关闭前钩子 **触发时机**:用户点击关闭按钮或调用 `app.quit()` 之前 **实现位置**: - 注册:[electron/main.js](electron/main.js#L13) - 实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js#L42-L44) ```javascript // 注册 app.register("before-close", life.beforeClose); // 实现 async beforeClose() { logger.info('[lifecycle] before-close'); // 在这里可以做: // - 保存用户数据 // - 关闭数据库连接 // - 清理临时文件 // - 释放系统资源 // - 确认是否退出 } ``` **适用场景**: - ✅ 保存应用状态 - ✅ 关闭数据库连接 - ✅ 清理临时资源 - ✅ 询问用户是否确认退出 - ✅ 上传日志或统计数据 **阻止关闭示例**: ```javascript async beforeClose(args, event) { const { dialog } = require('electron'); const result = await dialog.showMessageBox({ type: 'question', buttons: ['取消', '退出'], message: '确定要退出应用吗?' }); if (result.response === 0) { // 阻止关闭 return false; } // 允许关闭 return true; } ``` --- ### 8. quit - 应用退出 **触发时机**:`beforeClose` 完成后,应用进程终止 **说明**: - 这是最终状态,不可逆 - 所有资源清理应在 `beforeClose` 中完成 - 退出后进程结束,无法执行代码 --- ## 额外生命周期:preload ### preload - 预加载模块 **触发时机**:应用启动时,在所有其他钩子之前 **实现位置**: - 注册:[electron/main.js](electron/main.js#L16) - 实现:[electron/preload/index.js](electron/preload/index.js#L7-L9) ```javascript // 注册 app.register("preload", preload); // 实现 function preload() { logger.info('[preload] load 1'); // 在这里可以做: // - 加载环境变量 // - 注册原生模块 // - 设置全局异常处理 } ``` **适用场景**: - ✅ 加载环境变量 - ✅ 注册 Node.js 原生模块 - ✅ 设置全局错误处理 - ✅ 初始化第三方 SDK --- ## 实际开发示例 ### 示例 1:数据库初始化 ```javascript // electron/preload/lifecycle.js const Database = require('better-sqlite3'); let db; class Lifecycle { async ready() { // 在 ready 钩子中初始化数据库 const path = require('path'); const dbPath = path.join(app.getPath('userData'), 'app.db'); db = new Database(dbPath); logger.info('[lifecycle] database initialized'); } async beforeClose() { // 在关闭前关闭数据库连接 if (db) { db.close(); logger.info('[lifecycle] database closed'); } } } ``` ### 示例 2:自动更新检查 ```javascript // electron/preload/lifecycle.js const { autoUpdater } = require('electron-updater'); class Lifecycle { async windowReady() { // 窗口就绪后检查更新 const { getMainWindow } = require('ee-core/electron'); const win = getMainWindow(); autoUpdater.checkForUpdates(); autoUpdater.on('update-available', () => { win.webContents.send('update-available'); }); logger.info('[lifecycle] update check started'); } } ``` ### 示例 3:托盘图标 ```javascript // electron/preload/lifecycle.js const { Tray, Menu } = require('electron'); let tray; class Lifecycle { async electronAppReady() { // 在 Electron 就绪后创建托盘 const path = require('path'); const iconPath = path.join(__dirname, '../../public/images/tray-icon.png'); tray = new Tray(iconPath); const contextMenu = Menu.buildFromTemplate([ { label: '打开主窗口', click: () => { const { getMainWindow } = require('ee-core/electron'); getMainWindow().show(); }}, { label: '退出', click: () => { const { app } = require('electron'); app.quit(); }} ]); tray.setContextMenu(contextMenu); logger.info('[lifecycle] tray created'); } } ``` ### 示例 4:无白屏启动 ```javascript // electron/config/config.default.js windowsOption: { width: 1200, height: 800, show: false, // 关键配置:初始不显示 backgroundColor: '#ffffff' } // electron/preload/lifecycle.js class Lifecycle { async windowReady() { // 页面加载完成后再显示,避免白屏 const { getMainWindow } = require('ee-core/electron'); const win = getMainWindow(); win.once('ready-to-show', () => { win.show(); win.focus(); }); } } ``` --- ## 生命周期执行顺序总结 ``` 1. preload() # 预加载模块 2. new ElectronEgg() # 创建应用实例 3. ready() # 核心框架就绪 4. electronAppReady() # Electron 就绪 5. [创建主窗口] # 框架内部创建窗口 6. windowReady() # 窗口就绪 7. [应用运行中] # 正常运行状态 8. beforeClose() # 关闭前钩子 9. [应用退出] # 进程结束 ``` --- ## 注意事项 1. **异步支持**:所有生命周期钩子都支持 `async/await`,可以执行异步操作 2. **错误处理**:建议在每个钩子中添加 try-catch 错误处理 3. **顺序依赖**:不要在早期钩子中访问尚未初始化的资源(如在 `ready` 中操作窗口) 4. **性能优化**:避免在钩子中执行耗时操作,会阻塞应用启动 5. **资源清理**:在 `beforeClose` 中务必清理所有资源,避免内存泄漏 6. **日志记录**:建议在每个钩子中记录日志,方便排查问题 --- ## 相关文件 - 生命周期注册:[electron/main.js](electron/main.js) - 生命周期实现:[electron/preload/lifecycle.js](electron/preload/lifecycle.js) - 预加载模块:[electron/preload/index.js](electron/preload/index.js) - 窗口配置:[electron/config/config.default.js](electron/config/config.default.js) --- ## 参考资源 - ElectronEgg 官方文档:https://www.kaka996.com/pages/987b1c/ - Electron 官方文档:https://www.electronjs.org/zh/docs/latest/