{T}

Electron 主进程 app 生命周期、平台差异与核心方法

概述

这一节细讲主进程的 app 模块:它管的是“整个桌面应用”的生命周期,而不是某个页面的生命周期。课程用最实用的办法——把关键事件都挂出来打印一遍——帮你看清启动和退出的执行顺序,再讲清 quit()exit() 的语义差异、macOS 下“关窗不退应用”的平台习惯,以及为什么读文档必须看平台标签。它把主进程线从“能启动”推进到“理解应用级生命周期”。

学习目标

  • 理解 app 管的是应用级生命周期,是主进程最核心的入口之一。
  • 会用“把生命周期事件挂出来打印”的方法观察 will-finish-launching → readybefore-quit → will-quit → quit 的顺序。
  • 区分 quit()(走完整生命周期)与 exit()(更接近直接终止进程)。
  • 理解 window-all-closed 在 macOS 上“关窗不退应用”的默认行为及经典判断模板。
  • 掌握 activate 事件如何为 macOS 的窗口恢复补链,并与 window-all-closed 配套。
  • 建立“平台意识”:读 Electron 文档时主动看 API 后的平台标签,很多接口不是全平台通用。

一、app 模块管的是“整个应用”的生命周期

在 Electron 里,页面有页面的生命周期,窗口有窗口的生命周期,但更外层还有“整个应用”的生命周期,这一层主要由 app 管。课程把 app 作为主进程模块讲解的第一站非常合理,因为后面课程都会围绕它去看:应用什么时候准备好了、什么时候将要退出、什么时候所有窗口都关了、在不同平台上退出行为是否一样。app 的定位不是“普通对象”,而是整个主进程侧最基础的状态总线之一;学 Electron 时,appBrowserWindow 是最该优先熟悉的两个主线 API。

二、理解生命周期最有效的方式:把事件挂出来打印顺序

这一节课程没有直接背文档,而是用了最实用的学习方法:把几个关键事件都监听起来,然后在控制台里看执行顺序。课程重点观察的是 will-finish-launchingreadywindow-all-closedbefore-quitwill-quitquit 这几个事件。这种学习方式特别适合 Electron,因为生命周期很多名字都很像,如果只是背定义,后面很容易混;但一旦自己跑一次,你就会清楚应用启动时先有什么、关闭时后面依次有什么。课程最后明确看到:启动时最关键的是 will-finish-launching → ready,退出时是 before-quit → will-quit → quit

ts
app.on("will-finish-launching", () => console.log("will-finish-launching"))
app.on("ready", () => console.log("ready"))
app.on("before-quit", () => console.log("before-quit"))
app.on("will-quit", () => console.log("will-quit"))
app.on("quit", () => console.log("quit"))

这里还值得记一个细节:主进程日志通常出现在终端或 VS Code 调试控制台,而不是在页面 DevTools 里——这间接说明主进程是一个 Node 侧的应用,它和页面渲染上下文是两回事。

三、quit() 与 exit() 语义完全不同

课程特意对比了 quit()exit(),这两个方法最容易让新手误以为只是名字不同。真正的区别在于:quit() 会触发正常退出流程,before-quitwill-quitquit 这些事件都会参与;而 exit() 更接近“直接退出进程”,不会优雅地走完前面的那些生命周期。所以只要你后面涉及清理资源、存储状态、保存窗口信息,就必须知道自己该走哪一种退出方式。

ts
function closeAppGracefully() {
  app.quit() // 走完整生命周期,事件都会触发
}

function forceExit() {
  app.exit() // 更接近直接终止进程
}

需要走清理流程时,优先考虑 quit()exit() 更适合非常明确的强制终止场景。课程这里是在提前给后面“窗口状态保存 / 资源清理”埋理解基础,别等到要存数据时才发现退出的姿势不对。

四、window-all-closed 的 macOS 差异与经典模板

这一节最值得注意的实践点,就是平台差异。老师专门讲了:在 macOS 上,用户把所有窗口都关掉之后,应用往往并不会完全退出,它还会留在 Dock 上——这和 Windows / Linux 用户的直觉不同。所以课程里才会在 window-all-closed 里加一层判断:如果当前平台不是 darwin,才真正 quit()

ts
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit()
})

这段代码几乎是 Electron 项目里的经典模板之一。它本质上不是在“奇怪写法”,而是在尊重不同桌面平台用户的原生操作习惯。桌面端开发不能只站在单一平台的直觉上写逻辑——macOS 用户习惯里,“关掉所有窗口 ≠ 退出应用”,这段代码正是对这种习惯的对齐。

五、activate:为 macOS “窗口没了应用还活着” 补链

课程在讲完 window-all-closed 的平台差异后,紧接着就补了 activate。这一步非常自然:一旦你遵循了 macOS 的行为(关闭窗口不退出应用),那么用户下一次再点 Dock 图标时,就必须有一段逻辑去重新把窗口创建出来。课程给出的写法是:在 app.on("activate") 里检查当前有没有窗口,如果没有,就 createWindow()

ts
app.whenReady().then(() => {
  createWindow()
  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

这是 macOS 体验里很关键的一条补链逻辑。没有它的话,你就会得到一个“应用还活着、但怎么点都没有窗口出来”的奇怪状态。activatewindow-all-closed 往往是一组配套逻辑,只要你想支持 macOS 的原生体验,这一段基本绕不开。

六、读文档要看平台标签,很多 API 不是全平台通用

课程后面专门带大家看了一眼官方文档的 API 页面,并强调:文档上的平台标签非常重要。因为很多 app 事件、方法或属性,并不是 Windows / macOS / Linux 全都一样——某些只对 macOS 生效,某些只在 Windows 和 macOS 有。这说明看 Electron 文档时,不能只看 API 名字和参数,还要顺手扫一眼这个接口到底支持哪些平台;不然你很容易在 Linux 或 Windows 上测试时,以为自己代码写错了,实际上只是平台不支持。

ts
type ElectronApiPlatform = "macOS" | "Windows" | "Linux" | "All"

桌面端和 Web 不同,平台差异是天然存在的。文档标签不是装饰,它直接关系到你代码能否跨平台工作;课程这里是在提前训练大家的“平台意识”,后面遇到某个 API 在你的系统上没效果时,第一反应应该是去查平台标签。

七、把生命周期落成一条主进程时间线

如果把这一节课抽象一下,它真正建立起来的是一条主进程生命周期时间线:will-finish-launching → ready → window-all-closed → before-quit → will-quit → quit → activate。课程不是只让你背这些名字,而是在帮你建立一种时间顺序感:应用启动时发生什么、所有窗口关掉时发生什么、真正退出时发生什么、用户重新激活应用时发生什么。

ts
type AppLifecycleTimeline = [
  "will-finish-launching",
  "ready",
  "window-all-closed",
  "before-quit",
  "will-quit",
  "quit",
  "activate"
]

有了这条时间线,后面你再去做窗口恢复、菜单初始化、资源清理、状态保存,就知道应该挂到哪里了。这节课最重要的不是某个 API,而是顺序感和平台差异感;主进程生命周期理解透了,后面很多 Electron API 都会自然归位。

八、这一节真正建立的是“应用级生命周期”心智

到这一节为止,Electron 的主进程线已经从“能启动”走到“理解生命周期”了。它真正交付的不是某几个事件名,而是一种主进程应用生命周期的心智模型:应用什么时候准备好、什么时候窗口全关、什么时候真正退出、什么时候重新激活。这种模型一旦建立,你就不会再对着一堆事件名发懵,而是能像排时间表一样,把初始化、清理、恢复等逻辑精确地挂到合适的钩子上。


常见问题

问题原因解决方案
关闭最后一个窗口后 macOS 上应用还在这是平台习惯,不是代码坏了window-all-closed 中对 darwin 做条件判断
quit()exit() 有什么区别一个走完整生命周期,一个更接近直接终止需要清理和保存状态时优先用 quit()
activate 为何总和 window-all-closed 一起出现二者共同组成 macOS 窗口关闭 / 重新激活链路通常在 whenReady() 后一起配置
主进程日志为何不在页面 DevTools 里主进程不是页面上下文看终端或 VS Code 调试控制台
某 API 在当前系统上没效果可能本身就不是全平台支持看官方文档里的平台标签
生命周期事件名字太像怎么记单纯背定义容易混把事件挂出来打印,观察真实执行顺序
怎么知道某事件在退出前还是退出后退出链是 before-quit → will-quit → quit按“退出前 / 中 / 后”三阶段理解

延伸阅读