Electron 创建窗口与应用生命周期
🎯 引言
窗口不是随时都可以创建的普通对象。Electron 需要先完成应用初始化,应用退出时也要根据操作系统习惯处理窗口和进程。本篇只讲这些常用生命周期,不加入菜单和业务功能。
学完本篇,你将能够:
- 在正确的时机创建
BrowserWindow。 - 处理 macOS 的重新激活行为。
- 让应用只运行一个实例。
- 保存并清理窗口引用。
🧱 创建窗口的正确时机
窗口应该在 app.whenReady() 完成后创建。主进程可以把创建逻辑放进一个函数,之后在首次启动和 macOS 重新激活时复用它。
const { app, BrowserWindow } = require('electron/main');
const path = require('node:path');
let mainWindow;
function createMainWindow() {
mainWindow = new BrowserWindow({
width: 960,
height: 640,
});
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
mainWindow.on('closed', () => {
mainWindow = null;
});
}
app.whenReady().then(() => {
createMainWindow();
});
保存窗口引用,是为了让主进程之后能够检查窗口是否存在、聚焦窗口或向窗口发送消息。窗口关闭后要清空引用,避免继续使用已经失效的对象。
🪟 处理激活和退出
常见的生命周期事件如下:
| 事件 | 常见用途 |
|---|---|
whenReady | Electron 初始化完成后创建窗口 |
activate | macOS 用户重新点击应用时恢复窗口 |
window-all-closed | 所有窗口关闭后决定是否退出应用 |
app.whenReady().then(() => {
createMainWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createMainWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
Windows 和 Linux 通常在所有窗口关闭后退出,macOS 则常常保留应用进程,等待用户从程序坞再次打开。这个差异由 Electron 应用事件配合操作系统平台判断完成。
🔒 让应用只运行一个实例
桌面应用通常不希望用户重复启动多个相同进程。requestSingleInstanceLock() 可以让后启动的实例退出,并通知已经运行的实例把窗口显示出来。
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
app.quit();
} else {
app.on('second-instance', () => {
if (!mainWindow) {
return;
}
if (mainWindow.isMinimized()) {
mainWindow.restore();
}
mainWindow.focus();
});
}
这段代码需要放在创建窗口之前。它只解决“只能有一个应用实例”,不会自动处理深链接、文件关联等后续数据传递问题。
🧾 小节总结
BrowserWindow应在app.whenReady()之后创建。- 把窗口创建逻辑封装成函数,方便启动和重新激活时复用。
- 窗口关闭后要清理引用。
- macOS 常在
activate时重新创建窗口。 requestSingleInstanceLock可以避免应用重复运行。
❓ 知识问答
Q1:为什么不能在文件加载时直接创建窗口?
A:Electron 的应用环境还没有准备完成,窗口创建可能失败或行为不稳定。应该等待 whenReady。
Q2:为什么要判断 mainWindow 是否为空?
A:窗口关闭后引用会被清空,发送消息或聚焦前需要先确认窗口仍然存在。
Q3:单实例锁和多个窗口冲突吗?
A:不冲突。单实例限制的是应用进程数量,一个应用进程仍然可以管理多个窗口。
🧪 小练习
把 createMainWindow 中的窗口宽度改为 1100,并在 second-instance 事件中补充窗口显示逻辑:
if (mainWindow) {
// 请在这里编写代码
}
🎉 现在应用已经有了稳定的窗口生命周期。下一篇开始让 Vue 页面通过 preload 和 IPC 调用主进程能力。
