Files
pqs-9100_client/doc/生命周期描述.md

11 KiB
Raw Blame History

ElectronEgg 生命周期详解

本文档详细说明 ElectronEgg 框架的应用生命周期机制及其在项目中的实现。

生命周期流程图

┌─────────────┐
│    new      │  创建 ElectronEgg 实例
└──────┬──────┘
       │
┌──────▼──────┐
│   ready     │  core app 加载完成ee-core 框架初始化)
└──────┬──────┘
       │
┌──────▼─────────────┐
│ electronAppReady   │  Electron app 加载完成
└──────┬─────────────┘
       │
       ├─────────────────┐
       │                 │
┌──────▼──────┐   ┌─────▼────────┐
│ mainWindow  │   │ windowReady  │  主窗口创建完成
└──────┬──────┘   └─────▲────────┘
       │                 │
       └─────────────────┘
       │
┌──────▼──────┐
│   running   │  应用运行中
└──────┬──────┘
       │
┌──────▼──────┐
│beforeClose  │  退出之前触发
└──────┬──────┘
       │
┌──────▼──────┐
│    quit     │  应用退出
└─────────────┘

生命周期钩子详解

1. new - 实例创建

触发时机:调用 new ElectronEgg()

实现位置electron/main.js

const { ElectronEgg } = require('ee-core');
const app = new ElectronEgg();

作用

  • 创建 ElectronEgg 应用实例
  • 初始化框架核心模块
  • 准备生命周期管理器

2. ready - 核心应用就绪

触发时机ee-core 框架加载完成Electron app 启动之前

实现位置

// 注册
app.register("ready", life.ready);

// 实现
async ready() {
  logger.info('[lifecycle] ready');
  // 在这里可以做:
  // - 初始化数据库连接
  // - 加载配置文件
  // - 初始化全局变量
}

适用场景

  • 初始化数据库连接
  • 加载应用配置
  • 注册全局服务
  • 初始化日志系统
  • 不能操作窗口(窗口还未创建)

3. electronAppReady - Electron 应用就绪

触发时机Electron 的 app.ready 事件触发后,主窗口创建之前

实现位置

// 注册
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 钩子

配置示例

// electron/config/config.default.js
windowsOption: {
  width: 1200,
  height: 800,
  show: false, // 设置为 false 可实现无白屏启动
  webPreferences: {
    contextIsolation: false,
    nodeIntegration: true
  }
}

5. windowReady - 窗口就绪

触发时机:主窗口创建完成,页面加载完毕

实现位置

// 注册
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() 之前

实现位置

// 注册
app.register("before-close", life.beforeClose);

// 实现
async beforeClose() {
  logger.info('[lifecycle] before-close');
  // 在这里可以做:
  // - 保存用户数据
  // - 关闭数据库连接
  // - 清理临时文件
  // - 释放系统资源
  // - 确认是否退出
}

适用场景

  • 保存应用状态
  • 关闭数据库连接
  • 清理临时资源
  • 询问用户是否确认退出
  • 上传日志或统计数据

阻止关闭示例

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 - 预加载模块

触发时机:应用启动时,在所有其他钩子之前

实现位置

// 注册
app.register("preload", preload);

// 实现
function preload() {
  logger.info('[preload] load 1');
  // 在这里可以做:
  // - 加载环境变量
  // - 注册原生模块
  // - 设置全局异常处理
}

适用场景

  • 加载环境变量
  • 注册 Node.js 原生模块
  • 设置全局错误处理
  • 初始化第三方 SDK

实际开发示例

示例 1数据库初始化

// 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自动更新检查

// 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托盘图标

// 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无白屏启动

// 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. 日志记录:建议在每个钩子中记录日志,方便排查问题


相关文件


参考资源