electron 跨平台桌面应用 关闭app,最小化到托盘,点击系统托盘的退出或强制退出才真正结束进程

93 阅读2分钟

常见桌面软件(WPS、网易云、钉钉等)都遵循同一套“最小化”规则:点窗口 × → 不是退出,只是 缩到系统托盘;在托盘 右键 → 退出(或任务管理器强制结束)→ 才真正 终止进程

那么问题来了,怎么实现这个功能呢?,其实主要就是区分普通关闭(标题栏菜单)还是 主动关闭(退出、强制退出),现在贴一下实现代码:

const {
  ipcMain,
  app: electronApp,
  globalShortcut,
  session,
  nativeTheme,
  BrowserWindow,
} = require("electron");

async windowReady() {
  let closeListener;
  // 是否是强制关闭软件
  let willQuitApp = false;
  /**
   * 关闭按钮行为:
   * 1. 点击窗口标题栏的 “×” → 仅隐藏到系统托盘,不退出进程。
   * 2. 在托盘图标右键菜单中选择 “退出” → 触发 `before-quit` 事件,真正结束进程。
   *
   * 区分逻辑:
   * - 收到 `before-quit` 事件:视为用户主动退出,执行完全关闭。
   * - 未收到 `before-quit` 事件(仅 `close` 事件):视为普通关闭操作,窗口隐藏到托盘。
   */
  win.on(
    "close",
    (closeListener = (e) => {
      e.preventDefault();
      console.log("willQuitApp",willQuitApp);
      if(willQuitApp) {
        win.show();
        win.focus();
        willQuitApp = false;
        win.webContents.send("before-app-quit", {});
        // electronApp.quit();
      } else {
        win.hide();
      }
    })
  );

  electronApp.on('before-quit', (e) => {
    console.log("强制退出");
    willQuitApp = true;
  })
  
  /**
   * 处理应用激活与单实例二次启动
   * ------------------------------------------------
   * activate        macOS 点击 Dock 图标 / Windows/Linux 点击任务栏图标
   *                 → 若窗口已隐藏或最小化,则恢复并聚焦
   * second-instance 单实例模式下再次启动应用(如双击桌面快捷方式)
   *                 → Windows 下解析命令行参数 argv,恢复并聚焦已有窗口
  */
  electronApp.on('activate', () => {
    if (win && !win.isVisible()) {
      win.show();
      win.focus();
    }
  });
  electronApp.on('second-instance', (event, argv) => {
    if (process.platform === 'win32') {
      if (win) {
        if (win.isMinimized()) win.restore();
        win.show();
        win.focus();
      }
    }
  })

  ipcMain.on("confirm-app-quit", (event) => {
    console.log("electron get message 'confirm-app-quit'");
    win.removeListener("close", closeListener);
    win.close();
    electronApp.quit();
  });
        }
      }
    });
  });
}

利用 willQuitApp 标记区分两种关闭场景:

  1. 主动退出(托盘菜单或快捷键)会触发 before-quit,立即置 willQuitApp = true
  2. 普通关闭(点 ×)不会触发 before-quitwillQuitApp 保持 false

close 事件中统一 preventDefault(),随后:

  • willQuitApp === true,检查是否有未保存文件;若有,向渲染进程发送 before-app-quit,让前端弹窗提示“上传并退出”或“直接退出”;用户确认后再正式退出。
  • willQuitApp === false,仅隐藏窗口到托盘。

activate 和 second-instance 则是在app隐藏到托盘后处理处理应用激活与单实例二次启动的方法,ok这样就可以了