Electron 窗口卡顿问题的处理
最近发现使用 Electron 做跨平台桌面软件开发是一种很有意思的体验:只要你熟悉最基础的前端知识,在配合 Electron Document 就可以开发跨平台的桌面软件,想想还是很香的。
Electron 在其 官方 demo 中提供了常见桌面软件大部分基础性事件处理模式,包括:BrowserWindow
的创建及切换、系统菜单管理、main process
以及 renderer process
之间的通信、系统托盘处理、消息通知体系等。如果想要快速了解并掌握 Electron 开发的话,相信这应该是最好的教材。
electron api demo
然而,在官方提供的 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()
}
})
至于为什么一定要这样做,就留给大家去猜吧!能看懂的人,会懂。