一次Electron桌面端的开发总结

5,851 阅读11分钟

最近做的是一个客户端项目,不算大型,但涉及到的内容范围却挺广,也踩过很多坑。

目前项目开始收尾了,便来总结一下桌面端开发的方案。可以把这看作是一个Electron的开发方案或者设计思路,无论你是刚好有项目需要用到桌面端开发,还是为将来作知识储备,相信本文对你一定会有帮助。

框架选项

不得不说现在桌面端的框架真是越来越多。一方面,从技术的角度来看,接触新的技术当然对我们有好处。但另一方面,从项目业务角度考虑,学习成本也是一个权重很高的因素。

作为一个前端团队,我们初步挑选出下列热门框架来进行对比:

image.png

考虑再三之后,我们还是选了Electron。原因也很简单:

  • 对前端开发团队的融入友好
  • Electron有着成熟的生态,在构建、更新等环节都有成熟的选择。
  • 良好的兼容性
  • 在我们的业务中,应用的体积并不是什么大的问题。

数据存储

持久化的数据存储是桌面端应用的基本操作,而选用Electron的好处之一就是,在数据存储的选择上我们可以直接使用浏览器的存储技术。不及如此,一些基于NodeJs的数据模块结合操作系统之后,可以实现更多定制化的需求。

下面是我对数据存储层面的总结:

LocalStorage

通常localStorage都会是我的首选,因为能在BrowserWindow完成的IO,一定比需要跨其他进程才能完成的IO更快。

但是LocalStrorage的缺点同样很明显:

  • 只能存字符
  • 以及有容量大小的限制。
  • 有被XSS盗取的风险

因此,一般我会把一些应用软件级别的信息存在LocalStorage,这类信息主要用于记录应用的状态,一般不会直接与用户敏感信息有关而且容量不大。如主题颜色,上次(检查)更新时间等。

当然如果有需要的话,你也可以用Electron提供的safeStorage API对信息加解密。


本地持久化

本地持久化是我用得最多的数据存储方式,这是由我的业务需求决定的,并不一定适合所有人。所谓本地持久化,其实就是通过Main进程直接把数据读写在用户的硬盘中。因此,理论上无论什么类型的数据都可以存。

而我的认为它最大的优势是,用户可以直接打开看到里面的内容。因此日志这类的数据就非常适合。同时你可以选择文件的写入路径是在应用包内,还是用户的HOME目录,由此实现控制文件数据在下次安装后是否仍在存在。

当然,这种方式的优点也是它最大的缺点。过于暴露的信息,有可能会被用户误删除或者解密敏感信息。因此使用时需要权衡好。


indexDB

indexDB是你在浏览器可以用到的成本最低的数据库。相比LocalStorage,它可以支持存放文件,而不受容量大小限制。通常来说一些有数据库要求的数据都可以存这里,如浏览记录等。

而使用indexDB的风险有:

  • 不区分域名。如果你的应用有多个不同URL的renderer进程,就要留意加上对应的命名空间了。
  • 需要关注用户的硬盘剩余容量。默认情况下,当用户的电脑硬盘容量过低时,Chrome有可能会自动清空indexDB的数据。因此开发者需要关注用户的硬盘容量,或者用StorageManager.persist()保持数据。

相关说明:developer.mozilla.org/en-US/docs/…


第三方库

既然以及用了Electron,我们自然可以用一些第三方库。这样的好处是可以针对我们的业务需求,实现高定制化的操作。而选用这些库时,应该要从性能,兼容性,安全性这几方面考虑。这里简单举几个目前业内用得比较多的库,大家可以自行了解:

另外我也看过有人会使用像MongoDB的库。这种库相对会更复杂,因为需要考虑用户的安装操作。


离线环境考虑

如果一个应用是必须在线运行的,如网游、聊天工具、远程控制工具等,对于很多数据你可以只要关心最新的即可。因为对于用户来说,旧的数据对他没有意义。但如果应用是可以离线运行的,情况就会很不一样,开发者必须做更多的考虑:

  • 本地数据与线上数据冲突、合并的情况。
  • 离线数据是否足够安全。
  • 线上数据更新后,应用对本地旧数据的兼容。

下面我们逐一讨论:

数据冲突、合并

这种情况是最常见的,用户在弱网或者离线环境下,会与服务器失去数据同步,这时如果用户继续操作应用,本地数据会继续更新。当网络恢复时,应用再次与服务器数据同步,就会出现数据冲突。

假如应用数据是单个设备独享的,我们只要判断哪个数据是最新的即可。因此在数据设计的时候,应该要加上数据的最后更新时间,以便做同步操作时的判断。

但是,如果数据允许多设备之间共享,开发者则需要加入更多的考虑。这种场景也可能发生A设备上离线运行一段时间后,在B设备又离线运行。然后在B设备网络恢复时,同步数据在服务器。最后回到A设备在线运行时,同步数据就会被认为不是最新的数据,因此丢失。

面对这种情况,我们把它当作协同工具或者git仓库去理解就会明白,我们需要一个diff的过程。在客户端每次记录的操作都可以生成一个uuid,这样就可以区分不同设备的操作。然后服务器每次同步数据时,都要进行diff操作。区别是不能再简单地用更新时间作判断,而是用操作的uuid判断客户端的数据是否与服务器的一致。实现数据同步。

诚然,diff操作的开发对不少团队来说,都会是一个很复杂的过程。因为团队需要完成从数据设计、diff逻辑、再到大量测试的操作才能确保产品的稳定。这种规模的开发在小团队的产品初期会是一个压力很大的挑战。因此,我认为这作为一个后期迭代的功能,在初期开发者可以考虑使用一些UI提示来告知用户:当前操作为离线环境,数据未同步。并为用户提供手动同步的按钮,这样用户就会明白他需要先同步数据,才能在其他设备中看到。


数据安全

桌面端的数据安全是一个很大的范畴,从单机游戏和付费应用被破解就可以看出。对于有意破解应用的人,总能找到破解的办法。

当然,我个人这此并不是很专业,最好的建议是:不要在本地存敏感数据。因为一切数据只要是存在了本地,用户都有可能破解。特别是Electron在这方面的保障更为薄弱(HTML和JS文件都可以直接访问)。


数据结构更新

这种情况是,服务器的数据结构被修改了,用户的应用在请求服务数据之后,发现无法解析,导致应用出错。

这在某种程度上属于生产事故,是一种很罕见的情况,只要团队前期把数据结构设计好就不会出现。针对这种情况,最好的解决方式是客户端和服务器同时升级。也就是说一旦服务器的数据结构改变了,应用在无法解析之后,应该立即检查是否可以更新,然后通过UI告知用户必须更新才能继续使用。这其实也是一种兜底的方案,我们还是希望这种不要发生的为好。


硬件调用

某些桌面端的应用会有硬件调用的场景,如远程桌面,直播软件等。对于这类只用到摄像头和麦克风的应用,Electron有一个天然优势,就是可以使用WebRTC。

WebRTC为开发者提供了一套完整的音视频解决方案。有兴趣了解的朋友,可以在官网:webrtc.org了解,也可以看看我之前的文章《浅谈WebRTC》

对于其他硬件的调用,或者虽然是用到摄像头但数据流是经过处理后显示,而不是直接读取的情况。WebRTC就满足不了你了,此时你可以尝试直接在Main进程中用NodeJS处理。但如果NodeJS处理不了(也是我遇到的情况),就得用C/C++了。得益于NodeJS可以直接调用C的服务,我们只要先用C写好硬件的调用并提供API,这样在NodeJS层只要直接调用API即可。但这种情况,你的团队里还需要一位C++工程师。

最后,我想推荐一个库:usb-detection 。它可以查找用户电脑中有没有某个指定的硬件,或者监听硬件的插拔。

崩溃处理

应用的崩溃是开发者最不想看到的,因为即使有问题,也很难复现。因此在开发时,应该要做好这方面的处理。Electron本身为我们提供了一套方案:crashReporter

使用crashReporter,之后我们需要在服务端搭建对应的服务用于接受信息。而客户端的调用开发者可以自行选择在Main还是Renderer进程中进行。但是crashReporter只能处理崩溃,闪退的情况,真的一些应用层面的出错,我们还需要自己处理。

在Renderer中的可以onerror处理:

window.onerror = function(message, source, lineno, colno, error) { ... }

而在Main中,开发者需要用对应的try...catch逻辑捕捉可能出现的逻辑错误。

如果不想用现有的方案,自己另外开发一套崩溃的捕捉方案。开发者可以要关注更多的事件。

如BrowserWindow的事件:

  • close:窗口准备关闭。
  • closed: 窗口已经关闭。

app的事件:

  • render-process-gone: render进程的闪退等,可以根据具体的标识判断。
  • child-process-gone: 子进程的闪退等,可以根据具体的标识判断。
  • before-quit,will-quit,quit: app退出的生命周期事件,可以做日志本地保存,上传至服务器等操作。

打包构建

Electron最大的特性就是允许跨平台运行,程序包的构建是可以说是开发的最后一步。Electron的生态同样为我们提供了很多可选的构建工具:

Electron Forge

官网地址:www.electronforge.io/

Electron Forge是官方文档中推荐的构建工具,内置的其实是老牌elecron构建工具——electron-packager。它可以构建出各主流平台的程序包,以及整合在webpack中,实现项目开发、构建一体化。

如果没有特殊情况,Electron Forge应该是可以满足大量场景的。


electron-builder

官网地址:www.electron.build/

electron-builder目前在使用人数上远比Electron Forge多,迭代的次数也更频繁。相比Electron Forge,electron-builder可以支持更多平台的构建,如NSIS等。配置上也更丰富,并提供大量hook供开发者实现更多定制化操作。

总结

本文主要从前端开发者的角度,对桌面端开发的思考。我们从框架选型、数据存储、离线考虑等多个实际场景,总结出一系列的可选解决方案。希望可以为你提供一些整体的思路,以及更多的考虑因素。

如果你觉得本文对你有一点帮助,麻烦给我点个赞吧~~ 谢谢