Electron 在其 官方 demo 中提供了常见桌面软件大部分基础性事件处理模式,包括:BrowserWindow 的创建及切换、系统菜单管理、main process 以及 renderer process 之间的通信、系统托盘处理、消息通知体系等。如果想要快速了解并掌握 Electron 开发的话,相信这应该是最好的教材。
然而,在官方提供的 demo 中有两段个人认为处理的并不是特别好的代码。首先是创建窗口时对 close 事件的响应:
function createWindow() { mainWindow = new BrowserWindow(windowOptions) //..... mainWindow.on('closed', () => { mainWindow = null }) }
按照 Electron 提供的文档中对 close 的说明:close 事件 会在当前窗口关闭时触发。上面给出的代码中,对窗口的关闭事件响应为:将窗口对象置空。这种处理方式看似没有问题,但在 macOS 系统中窗口被关闭时,一般情况下的默认操作并不是停止运行软件,而是在 Dock 中保留软件以备用户再次点击时恢复。为了解决这个问题,官方 demo 中又引入了 activate 事件,其代码处理方式为:
app.on('activate', () => { if (mainWindow === null) { createWindow() } })
每次用户点击窗口关闭按钮时,将窗口对象释放,再点击时又重新创造一次窗口。这样的处理方式实在不敢苟同。想象一下,如果创建窗口的过程较为复杂(eg:判断登录状态、执行部分逻辑判断、网络请求之后再做业务逻辑等等),在体验上就会出现问题。实际上,官方提供的 demo 运行起来,在 macOS 系统中重复关闭再打开时,就会出现非常明显的「卡顿」情况,而卡顿的原因自然是因为每次窗口重新创建时,系统开销是很大的。
那么,假如我们在窗口关闭时,只是将其隐藏处理呢?
mainWindow.on('close', function (event) { event.preventDefault() mainWindow.hide() })
如果 mainWindow 对象没有被销毁,在 activate 事件处就可以这样做:
app.on('activate', () => { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.show() mainWindow.focus() } })
实际上,让 window 对象一直存在与内存中直至 app 被销毁,并不是最佳实践。为了让代码能够更加严谨,可以深挖 Electron 的文档,在其中发现了 before-quit 事件。于是,修改上述的代码之后,最终的处理方式变成:
app.on('before-quit', function () { app.quitting = true }) mainWindow.on('close', function (event) { if (app.quitting) { mainWindow = null // 如果当前操作确实是需要关闭窗口的话,就将其置空 } else { event.preventDefault() mainWindow.hide() } })
至于为什么一定要这样做,就留给大家去猜吧!能看懂的人,会懂。