Electron启动出现白屏问题的解决方案
作者:前端周公子
对于 Web 开发者使用 Electron 构建桌面应用程序时,经常会遇到如上图所示的一个问题 —— 窗口加载过程中长时间白屏。在应用窗口创建完成到页面加载出来的这段时间里,出现了长时间的白屏,这个问题对于前端开发来说是一个老生常谈的问题,纯 Web 端可能就是异步加载、静态资源压缩、CDN 以及骨架屏等等优化方案,但是如果是开发 Electron 应用,场景又有些许不同,因此我们也不能完全按照通用的前端解决白屏的方案进行处理,本文就来探索基于 Electron 场景下启动白屏的解决方案。
问题原因分析
1. Electron 主进程加载时间过长
Electron 应用在启动时,需要先加载主进程,然后由主进程去创建浏览器窗口和加载页面。如果主进程加载时间过长,就会导致应用一直停留在空白窗口,出现白屏。
主进程加载时间长的原因可以有:
- 初始化逻辑复杂,比如加载大量数据、执行计算任务等
- 主进程依赖的模块加载时间长,例如 Native 模块编译耗时
- 主进程代码进行了大量同步 I/O 操作,阻塞了事件循环
2. Web 部分性能优化不足
浏览器窗口加载 HTML、JavaScript、CSS 等静态资源是一个渐进的过程,如果资源体积过大,加载时间过长,在加载过程中就会短暂出现白屏,这一点其实就是我们常说的前端首屏加载时间过长的问题。导致 Web 加载时间过长的原因可以是:
- 页面体积大,如加载过多图片、视频等大资源
- 没有代码拆分,一次加载全部 Bundles
- 缺乏缓存机制,资源无法命中缓存
- 主线程运算量大,频繁阻塞渲染
解决方案
1. 常规 Web 端性能优化
Web 端加载渲染过程中的白屏,可以采用常规前端的性能优化手段:
- 代码拆分,异步加载,避免大包导致的加载时间过长
- 静态资源压缩合并、CDN 加速,减少资源加载时间
- 使用骨架屏技术,先提供页面骨架,优化用户体验
- 减少主线程工作量,比如使用 Web Worker 进行复杂计算
- 避免频繁布局重排,优化 DOM 操作
以上优化可以明显减少 HTML 和资源加载渲染的时,缩短白屏现象。还是那句话,纯 Web 端的性能优化对于前端开发来说老生常谈,我这边不做详细的赘述,不提供实际代码,开发者可以参考其他大佬写的性能优化文章,本文主要针对的是 Electron 启动白屏过长的问题,因为体验下来 Electron 白屏的本质问题还是要通过 Electron 自身来解决~
2. 控制 Electron 主进程加载时机
Electron 启动长时间白屏的本质原因,前面特意强调了,解决方案还是得看 Electron 自身的加载时机,因为我这边将 Web 部分的代码打包启动,白屏时间是非常短的,与上面动图里肉眼可见的白屏时间形成了鲜明的对比。所以为了解决这个问题,我们还是要探寻 Electron 的加载时机,通过对 Electron 的启动流程分析,我们发现:
- 如果在主进程准备就绪之前就创建并显示浏览器窗口,由于此时渲染进程和页面还未开始加载,窗口内自然就是空白,因此需要确保在合适的时机创建窗口。
- 反之如果创建窗口后,又长时间不调用
window.show()
显示窗口,那么窗口会一直在后台加载页面,用户也会看不到,从而出现白屏的效果。
因此我们可以通过控制主进程的 Ready 事件时机以及 Window 窗口的加载时机来对这个问题进行优化,同样的关于加载时机我们也可以有两种方案进行优化:
- 通过监听
BrowserWindow
上面的ready-to-show
事件控制窗口显示
// 解决白屏问题 app.whenReady().then(() => { // 将创建窗口的代码放在 `app.whenReady` 事件回调中,确保主进程启动完成后再创建窗口 const mainWindow = new BrowserWindow({ show:false }); // 加载页面 mainWindow.loadURL('index.html'); // 在 ready-to-show 事件中显示窗口 mainWindow..once("ready-to-show", () => { mainWindow.show(); }); });
上述代码通过操作 app.whenReady()
和 BrowserWindow
的 mainWindow.once('ready-to-show')
这几个 Electron 核心启动 API,优雅地处理了窗口隐藏 + 页面加载 + 窗口显示等问题,详细流程如下:
将创建窗口的代码放在
app.whenReady
事件回调中,确保主进程启动完成后再创建窗口创建窗口的时候让窗口隐藏不显示
{ show: false }
,避免页面没加载完成导致的白屏窗口加载页面
win.loadURL
,也就是说窗口虽然隐藏了,但是不耽误加载页面通过
ready-to-show
事件来判断窗口是否已经准备好,这个事件其实就代表页面已经加载完成了,因此此时调用mainWidnow.show()
让窗口显示就解决了白屏的问题
- 通过监听
BrowserWindow.webContents
上面的did-finish-load
或者dom-ready
事件来控制窗口显示
app.whenReady().then(() => { // 将创建窗口的代码放在 `app.whenReady` 事件回调中,确保主进程启动完成后再创建窗口 const mainWindow = new BrowserWindow({ show:false }); // 加载页面 mainWindow.loadURL(indexPage); // 通过 webContents 对应事件来处理窗口显示 mainWindow.webContents.on("did-finish-load", () => { mainWindow.show(); }); });
此方案与上述方案的唯一区别就是,第一个使用的是 BrowserWindow
的事件来处理,而此方案通过判断 BrowserWindow.webContents
这个对象,这个对象是 Electron 中用来渲染以及控制 Web 页面的,因此我们可以更直接的使用 did-finish-load
或者直接 dom-ready
这两个事件来判断页面是否加载完成,这两个 API 的含义相信前端开发者都不陌生,页面加载完成以及 DOM Ready 都是前端的概念,通过这种方式也是可以解决启动白屏的。
最后解决完成的效果如下:
总结
从上图来看最终的效果还是不错的,当窗口出现的一瞬间页面就直接加载完成了,不过细心的小伙伴应该会发现,这个方案属于偷梁换柱,给用户的感觉是窗口出现的时候页面就有内容了,但是其实窗口没出现的时间是有空档期的,大概就是下面这个意思:
从上图以及实际效果来看,其实我们的启动时间是没有发生改变的,但是因为端上应用和我们纯 Web 应用的使用场景不同,它自身就是有应用的启动时间,所以空档期如果不长,这个方案的体验还是可以的。但是如果前面的空档期过长,那么可能就是 Electron 启动的时候加载资源过多造成的了,就需要其他优化方案了。由此也可以见得其实对于用户体验来说,可能我们的产品性能并不一定有提升,只要从场景出发从用户角度去考虑问题,其实就能提升整个应用的体验。
以上就是Electron启动出现白屏问题的解决方案的详细内容,更多关于Electron启动白屏的资料请关注脚本之家其它相关文章!