Electron 生命周期事件
常见软件有 ready close closed 这些事件,但 electron 的事件也非常的多,如果想要更加深入的了解 electron 整个生命周期的流程,需要对应用里面的生命周期,窗口的生命周期以及页面内的生命周期的时机有一个清晰的理解。
一图胜千言:
一、设计启动退出的事件介绍
这里把这些事件分成三部分,App 事件、BrowserWindow 事件以及 Renderer 进程中的 Web 事件:
1. App 事件
on('will-finish-launching', (event: Event) => {})on('ready', (event: Event, launchInfo: Record<string, any>) => {})on('open-file', (event: Event, path: string) => {})【macOS】
应该在 ready 之前对 open-file 进行监听。如果想要自己接管文件的打开,应该 event.preventDefault()
触发条件:
- 应用已经打开,并且通过扩展名或者 open 命令打开文件的时候,触发
- 拖放一个文件到 Dock 但应用还没有运行的时候触发
Windows 电脑中,需要通过主进程的 process.argv 进行解析
on('open-url', (event: Event, url: string) => {})【macOS】
事件 open-url 是处理通过系统通过 electron 应用打开 url 时触发,如果想要自己接管打开 url,应该 event.preventDefault() 。并且要在 info.plist 中定义 url scheme,原话是这么说的:Your application’s Info.plist file must define the URL scheme within the CFBundleURLTypes key, and set NSPrincipalClass to AtomApplication.
on('activate', (event: Event, hasVisibleWindows: boolean) => {})【macOS】
事件 activate 只会在【首次启动应用程序】、【尝试在应用程序已运行时】或【单击应用程序的坞站或任务栏图标时】重新激活它。
on('did-become-active', (event: Event) => {})【macOS】
事件 did-become-active 则会在切换到这个应用的时候触发,比如没有窗口的应用或者程序第一次启动。
on('session-created', (session: Session) => {})on('web-contents-created', (event: Event, window: BrowserWindow) => {})on('browser-window-created', (event: Event, window: BrowserWindow) => {})
创建一个窗口,都是依次以 session-created , web-contents-created , browser-window-created 创建。但不知道为什么在 app ready 事件后,又触发了事件 web-contents-created 。
on('browser-window-focus', (event: Event, window: BrowserWindow) => {})on('second-instance', (event: Event, argv: string[], workingDirectory: string) => {})on('window-all-closed', ()=>{})
这个事件只有一个窗口一个窗口的关闭后,才会触发,其它情况,比如 app.quit 、 cmd+q 或者菜单的退出,或者任何其它方式的退出软件都不会触发。
on('before-quit', (event: Event) => {})
如果应用关闭是被 autoUpdater.quitAndInstall() 发起的,那么在所有窗口触发 close 之后 才会触发 before-quit 并关闭所有窗口。
on('quit', (event: Event, exitCode: number) => {})
在 Windows 系统中,如果应用程序因系统关机/重启或用户注销而关闭,那么 before-quit 和 quit 事件不会被触发。
2. BrowserWindow 事件
on('close', (event: Event) =>{})on('closed', () => {})on('ready-to-show', () => {})还没有显示的时候就发起
3. Renderer 进程
window.onunloadwindow.onbeforeunload返回非 undefined 就会中断 主进程 close 事件document.addEventListener('DOMContentLoaded')document.addEventListener('visibilitychange')
二、退出场景
1. 正常退出
Cmd+q或者菜单中的退出按钮。app.quit()autoupdater.quitAndInstall()app.reluanch()
2. 异常退出
比如常见的在主进程调用 process.crash() 。
3. SIG 信号退出
我们常见的命令行退出软件的方式有 ctrl+c ,命令行给进程发送了 SIGKILL 信号,其实还有其它常见关闭进程的方式,可以通过 kill 对进程发送信号,比如 kill -s KILL 24567 或者 kill -9 24567 :
1 | 1 HUP (hang up) |
4. 具体的退出例子
- 正常命令行启动,通过
Cmd+q退出
1 | ==> app-event: will-finish-launching <=== |
==> window-event: closed <=== 这个事件最后才发送是因为 closed 完全是异步的,closed 不会因为没有执行完就阻塞,应用会把所有异步执行完后才退出进程。
可以通过流程图更加清晰的表达:
【流程图】
- 正常启动,通过
ctrl+c退出
1 | ... |
启动后,所有事件正常,但退出通过 ctrl+c 中断应用, electron 就只发出了 before-quit 事件。试试其它方法:
SIGHUP和正常的退出流程一致SIGINT效果同上,也是ctrl+c发出的信号SIGQUIT会导致整个应用完全没有任何反应就退出SIGABRT效果同上SIGKILL效果同上SIGSEGV效果同上,也是process.crash()发出的信号SIGTERM和正常的退出流程大概一致,也是app.quit()的退出方式
- 正常启动,通过
app.quit()退出,这里发现通过这种方式退出,有一些不太一样的地方:
1 | ... |
这里多了一个 unload 事件,也可以看出,其实整个应用通过 app.quit 退出才是正常的退出流程, cmd+q 也会导致部分流程会被忽略退出。然后使用 app.reluanch() 也是和 cmd+q 类似,不会触发 unload 事件。
- 正常启动,通过 beforeunload 中断退出
通过 beforeunload 中调用 event.returnValue = false 中断应用的退出和窗口的关闭:
1 | window.onbeforeunload = (event: Event) => { |
通过 cmd+q 会重复调用下面三个事件:
1 | ==> app-event: before-quit <=== |
如果是直接关闭窗口:
1 | ==> window-event: close <=== |
- 正常启动,通过 BrowserWindow
close事件中断,就只会触发close事件:
1 | ==> window-event: close <=== |
BrowserWindow
closed事件是没有 event 的。
- 正常启动,通过 app
before-quit中断,直接按cmd+q是不会关闭窗口。
1 | ==> app-event: before-quit <=== |
通过
ctrl+c或者上面的一些 SIG 信号关闭,是会忽视这个中断关闭的操作。
- 正常启动,通过 app
will-quit中断,直接按cmd+q会关闭所有窗口,但程序还是激活状态。
1 | ==> app-event: before-quit <=== |
通过
ctrl+c或者上面的一些 SIG 信号关闭,是会忽视这个中断关闭的操作。
三、启动场景
软件常见的启动有很多,比如通过 open 或者 Windows 的 start 启动,或者通过 url scheme 然后系统启动,或者拖动文档到 dock 或者 tray 启动。下面来说说启动软件后,软件参数和环境的路径。
1. 普通启动
一般双击软件启动,会经过 will-finish-launching 和 ready ,然后正常进入应用界面。
2. 单例模式下的应用启动
在应用启动的时候,检测 app.requestSingleInstanceLock() 是否是单例模式,如果是就退出,并且发送一个事件给 second-instance ,从这里再获取退出的应用的 argv 和 cwd。
其实这里检测是否是单例模式的方法是,看看
app.getPath('userData')下是否有 lock 文件。
3. 通过命令行启动
命令行执行 /path/to/app --arg1 value1 --arg2 value2 document/path ,会在应用启动的时候,通过 process.argv 和 process.cwd
4. 通过 url scheme 启动
通过 app.setAsDefaultProtocolClient('electron-test') 注册 url scheme,然后在 app 事件 open-url 的 url 参数获取 url 信息:
1 | ==> app-event: did-become-active <=== |
5. 通过拖拽到 dock 或者 tray 启动
通过 dock 拖拽启动,需要在 info.plist 中声明支持的文件类型,比如 electronBuilder 可以通过 extendInfo 字段中,声明 CFBundleDocumentTypes 注册支持的类型。
触发的是事件 open-file 。