本文基于海康威视桌面端技术专家刘晓伦在「RTC Dev Meetup • 杭州站丨大前端时代的业务架构和跨端实践」活动中分享内容二次整理。
以下正文:
今天要与大家分享 19 款桌面软件开发框架,我将它们分了四类,然后分别就每个类别做相应的介绍,希望通过今天的分享,帮助大家在开发的过程当中少走一些弯路。
01 传统桌面软件开发框架
首先我们来聊一聊传统的桌面软件开发框架。这个类别中包含大家常见的 Qt、wxWidgets、GTK、FLTK、Swing 和 JavaFX。这六个框架有一些共同点:它们的历史都很悠久,使用的开发者也很多,并且相应的社区也很成熟,其中包含了丰富的资料。此外,它们的功能都很强大,有大量成熟的案例,框架也很稳定。
但是它们用到的都是相对来说都是比较成熟的技术,所以跟新兴技术之间还是有所差距,所以使用起来比较困难。
1、Qt
Qt 的优点大家都可以在官网上看到,这里不再赘述,其中有一点是 Qt 对各操作系统都做了很完善的封装,比如网络、文件剪切板等,如果要用系统级的 API,Qt 可以提供的非常丰富的 API。
对于 Qt 的问题这里介绍一下我的感受,我认为 Qt 目前的问题是发展方向不太专一,无法提供某些较大的模块,在开发过程中可能会逐渐发现使用的模块不太合适,进而废弃这个模块,包括很多 API 也是这样,我之前用过的 Qt script 现在就已经被标记为废弃了。
另外,Qt 的商业授权是不太友好的。如果要开发一个商业应用,可能这里要注意一下,有很多国内的公司都是收到过 Qt 的律师函要求收费的。Qt 还提供了很多组件,其中有一些复杂的组件对于控制 UI 的细节是比较难的,这里就不再举例了。
Qt 还有很多的动态链接库,如果要做静态链接也是比较难的,当然社区中提供了一种办法,就是可以编译 Qt 的源码做静态链接,但是这就又要涉及版权的问题了。
2、GTK
GTK 框架比较偏 Linux,在 Linux 中有很多桌面应用都是用 GTK 开发的,在 Windows 下 GTK 相对较少,并且在 Windows 系统下的静态链接也是比较难的,甚至搭环境都比在 Linux 系统下要难得多。
另外,GTK 在 Windows 下也做了系统 API 的封装,但是在 Windows 下的封装比在 Linux 系统下的封装要少一些。也就是说,有些 API 在 Linux 下能用,而在 Windows 下是不能用的,这样的 API 不在少数。最后 GTK 的商业授权是非常友好的。
3、FLTK
FLTK 框架是 C++ 之父推荐使用的,它就是一个非常轻量的 GUI 框架,但是轻量意味着功能是不足的,它的功能比较少,几乎不做系统 API 的封装,大部分都要开发人员自己用 C++ 编写。这样导致开发人员在每个平台上都要重新编写,比如要开放一个跨平台的图片应用,那么在 Windows 下调用一些系统级的 API 进行实现,在 Mac 下可能又要实现一遍。
FLTK 的优点是它的产物很小,因此它编译出来的东西可以做到很小,而且性能也非常好(比前两个都好)。FLTK 的商业授权也很友好,可以支持静态链接。但是 FLTK 有一些不足,就是因为它是一个 GUI 框架,因此在绘制 GUI 组件这方面是不太好的。
我在使用 FLTK 的时候发现,在其中做动画控制、组件叠加(这时用到就是绝对定位,就需要对组件的层级进行控制)都会遇到问题。另外,在绘制圆角的时候,虽然 FLTK 提供了相关的功能,但是用户还要控制圆角的特殊大小、某一个圆角出现等,这些都要写非常复杂的代码。
4、wxWidgets
wxWidgets 跟前面 3 个框架有所区别,它是基于操作系统的 API 来做桌面应用的,也就是说,在 Windows 下开发一个桌面应用时,看起来就像是传统的 Windows 桌面软件的风格,在 Mac 下则是 Mac 的风格,而前面三个都有自己的自绘引擎。
也就是说,在前面三个框架中做一个按钮,不管用什么样的绘制方式,在三个平台下表现都是一致的,但是 wxWidgets 在三个平台上都是按照三个平台自己的 API 来绘制这个按钮的。wxWidgets 提供了非常多的操作系统的 API,并且可以做到静态链接,但小问题比较多。
5、Swing/JavaFX
Swing /JavaFX 是基于 Java 的技术栈来做桌面应用的,因为要依赖 JVM,所以系统 API 比较多,但性能会一般,这两个框架可能相对来说用得少一些。
如果大家在这几个桌面软件框架当中做选择的话,我个人推荐还是用 Qt。
02 新兴桌面软件开发框架
刚才我们说的一些框架可能用的技术比较陈旧,大家开发起来比较麻烦。新兴的框架,比如微软的 MAUI、谷歌的 Flutter Desktop 和 JetBrains 公司的 Compose Multiplatform,都是采用非常新的技术来做的,开发起来很容易,开发体验也非常好。
但是因为这几个框架都比较新,基本上都是今年发布的正式版(最早是 Compose Multiplatform 发布的,然后依次是 lutter Desktop 和 MAUI),所以社区相对来说不如前面几个框架成熟,资料和成功案例也相对来说少一些。
1、MAUI
MAUI 是用 XAML 是写界面和逻辑的,大家如果用 WPF 写过软件的话,应该会对这项技术很熟悉。MAUI 用 C# 编写业务逻辑,只兼容 Windows 操作系统和 Mac 操作系统。MAUI 在 Linux 下是社区提供的一套支持体系,可能会有一些问题。另外,MAUI 是依赖 .NET 框架的,API 比较多,并且支持移动端(这三个框架都是支持移动端的,新兴的开发框架都兼容移动端的开发)。
2、Flutter Desktop
Flutter Desktop 是使用 dart 编写界面逻辑的,它的组件比较丰富,并且支持 Win 10 操作系统(之前的操作系统就不太支持了),API 是比较少的,需要开发人员自己来写。
3、Compose Multiplatform
Compose Multiplatform 依赖 JVM,是用 Kotlin 编写逻辑的界面和逻辑的,有消息说将来可能会推出 Kotlin/native*,这样就可以不用依赖 JVM 了,但是这可能还比较遥远的事情。Compose Multiplatform 的组件也是比较丰富的,大家都知道 Java 社区中有很多框架、模块等都可以使用,并且也是支持应用端的。
在这几类框架里中我推荐大家使用 Flutter Desktop,虽然它们都是新兴的框架,所以很难说哪一个将来会更受欢迎,但是根据它们的共同点(技术更现代化、更加易用、资料较少、社区不成熟、成功案例较少、用户较少、不稳定),我还是推荐 Flutter Desktop。
03 基于浏览器的桌面软件开发框架
基于浏览器的桌面软件开发框架相对较多,比如 Electron、NW.js、CEF、Sciter、WebView2、webview 和 TAURI。其中使用 Electron 比较多,也已经非常成熟。它们的共同点是它们可以复用浏览器的技术,比如 html、JS 和 CSS 等一系列生态中的组件。
除此之外,它们还可以做非常绚丽的界面,因为 Web 技术发展这么年,CSS、html 等在标记和控制界面特性方面的技术都已经非常成熟,不像刚才提到的 FLTK 要做叠加动画效果都非常困难,如果用这些技术做这些界面会非常从容。但这些框架的功能也是有强有弱。
1、Electron/NW.js
Electron 和 NW.js 这两个框架是非常相似的技术,它们都是把 Chromium 与 Node.js 集成到一起,让开发者可以只使用前端技术开发桌面应用。如果要访问系统的 API,就用 Node.js 提供的 API,当然也有一些特殊的 API,比方创建桌面图标、访问剪切板、控制窗口大小等,是 Electron 框架本身提供的,而不是 Node.js 提供的,也不是传统 HTML 系统提供的,是在其中做了一些附加的操作,当然 NW.js 也有。
这两个框架的作者都有很深的渊源,这里就不细说了。我个人觉得 NW.js 实现起来相对来说比较巧妙,对开发者比较友好,而 Electron 维护更给力,社区也很庞大。
2、CEF/WebView2
CEF 和 WebView2 框架都是直接基于 Chromium 开发的。CEF 的历史比较悠久,而且更灵活,大家可以通过这个框架进行自由的控制,API 也更多。
WebView2 是微软的短期团队提供的一个框架,这个框架也是直接基于基于 Chromium 做的封装,它提供了 .NET 和 C++ 的 API,大家可以用 .NET、C++ 或者 C#语言去操作这个框架,目前还不支持 MAC(将来可能会支持)。
相对来说 CEF 更像作者个人在维护,WebView2 就更像一个团队,但是因为 WebView2 不开源,所以也没法参与。
3、webview/TAURI
webview 和 TAURI 使用操作系统内配置的浏览器核心,比如应该在 Windows 下部署,那么就用 WebView2;如果在 Mac 下部署,就用 WKWebView;如果在 Linux 下部署,就用 webkitgtk 来做渲染,因此这里可能就会有一些兼容性的问题。
在做前端的时候经常会碰到一些兼容性的问题,但是目前来看,因为它们都是比较现代的浏览器核心了,所以这个问题还没有这么明显。另外,TAURI 是使⽤ Rust 开发的,如果要选择这个框架,可能还得熟悉 Rust 语言。
4、Sciter
Sciter 是一个比较特殊的框架,这里把它单列出来,Sciter 做了很多的削减,可以把产物的体积缩减到 10M 以内,前面几种框架基本上只要把浏览器核心分发给用户,即使压缩过后也得 60M。
Sciter 以前有一个自己的脚本叫 TIscript,现在用的是 QuickJS,它的功能非常强大,而且集成了 skia 的核心版本。我不知道大家有没有关注过一个叫作 rustdesk 的项目,之前它选择的就是 Sciter 框架,当然这个框架的小问题也会比较多。
如果大家选择这类框架,我推荐使用 Electron/CEF 框架。如果团队中没有 C++ 开发人员,或者不易于使用 C++ 进行开发应用,就选择 Electron;如果有 C++ 开发人员,而且应用非常注重性能,就选择 CEF。
04 即时渲染桌面软件开发框架
即时渲染相对应用的是保持模式的桌面应用框架,一般做桌面应用都是哪里需要更新,就更新哪里,框架会进行渲染。但是即时渲染是不一样的,它是每刷新一帧,就会把显示的内容都更新一遍,也就是说每一帧都会全部更新一遍。
所以这类框架有一个共同的特点,就是它们要消耗 CPU 和 GPU 资源,传统框架的配置都是保持模式,如果不更新是不会做渲染的。另外,这种框架与游戏应用可以无缝对接,但是传统应用案例比较少,小问题比较多,且尚在发展中。
1、Dear ImGui
即时渲染桌面软件开发框架中,Dear ImGui 比较流行,最近作者在开发 Dock 模式,估计今年有可能会推出,到时候大家可以去尝试一下。
它的开发方式是比较特别的,就是每个循环都在做渲染。比如使用 Dear ImGui 做事件开发时,会将某一个变量设置为 true 或者 false, 在这一轮渲染的过程当中是 true,在它下一轮渲染过程当中,我发现变成 false 了,我们就认为触发了某个事件。而不是像 web 开发领域中,当出现一个 event 时,另一个组件就可以接收到它。
2、Nuklear
Nuklear 是使⽤ C 语⾔开发的,更贴近 C 开发者的习惯,⽤户相对 Dear ImGui 更少、⼩问题更多。
3、RmIui
RmlUi 比较特殊,它不是即时渲染框架,但是却有即时渲染框架的通病,就是用它开发应用之后会持续消耗 CPU 和 GPU 资源,它可以使用 HTML 和 CSS 描述界面,上手相对比较难,开发非常灵活,界面也很灵活,资源消耗相对更多。
我最近一直在用这个框架,也跟作者进行了深入的交流。这个框架开发出来的应用程序可以做到 2M 左右,它可以解析你的 HTML,做一些非常特殊的效果,比如阴影、渐变之类的动画,但是它很小,不像浏览器一样,即使压缩也得 60 多兆,这也是它的优势。
这几类框架中我个人推荐 RmlUi,如果到作者的项目中进行提问,他基本上隔天就会回复。
05 总结在做总结之前,我先简述一下这几个框架的对比。
其实我们在介绍传统的桌面开发框架的时候,Qt 和 wxWidgets 内置的组件也有基于浏览器的, 就是 QWebEngin 和 wxWebView,QWebEngin 封装的是 Chromium 的核心,其实与 CEF 和 QWebEngin 挺像的,但是很多人在用 Qt 的时候,是把 CEF 集成到 Qt 中,反而不用 QWebEngin。
他们坚持的观点就是 QWebEngin 还不成熟,性能力相对来较弱,不如 CEF,但是目前来看,我个人认为,在 Qt 6.2 和 Qt 6.3 之后,QWebEngin 的 API 会更多,应用的需求更容易满足。
wxWebView 更像是 webview 和 TAURI,它也是在不同的操作系统上使用操作系统的浏览器核心,这里就不多说了。
再看一下 RmlUi 和 Sciter 的对比。RmlUi 可以用 html 和 CSS,Sciter 也可以用 html 和 CSS,因为 Sciter 集成了 QuickJS,所以它也可以写 JS 代码(但是也得写 C++,因为你不可能不用操作系统的 API,但是它的 C++可能会比 RmlUi 的 C++ 代码量要少很多)。两个框架都不支持全部的 html 和 CSS 规范。
如果要选一个桌面软件开发框架,应该确认侧重哪方面的能力,我认为有三方面的能力是需要注重的:
- 界面的描述能力:比如布局、元素定位、圆角、阴影、渐变
- 事件处理能力:比如鼠标事件、键盘事件、触屏事件、媒体播放结束、网络状态变更等
- 异步处理能力:处理业务逻辑的时候,界面渲染工作不能暂停
另外,桌面软件开发框架各有各的优势,比如软件开发逻辑很复杂,而且要快速地完成,那么 BrowserCore 可能是一个不错的方式,因为用前端技术来编写速度更快,但是如果软件需要更少的资源消耗和更快的运行速度,那么就需要考虑 Native 方式。
最后,要做桌面应用开发需要了解一些底层知识,比如多线程、多进程的控制;各种通信协议;⽇志收集;版本控制;设计模式与架构原则;本地数据控制;操作系统。
06 问答环节1、用 Electron 怎么处理 crash?
Electron 也有非常多问题,它也会崩溃,并且有崩溃报告的收集方式,收集这些报告之后还要进行分析,但是分析出来的崩溃报告实并不能很明确地反映到底是哪一行代码、哪一段业务出了问题。很多时候崩溃报告中可能更多的是说明一个指针指向的内存出现了问题,这时一种方法是尽量联系用户复现问题;第二种方式是做大量的自动化测试,收窄问题的范围,将其局限在一定的范围内再做精细化处理;第三种方式就是做 AB 测试。
2、Electron 开发的应用安装包太大这个问题有没有好的解决办法。另外,开发者该如何开发 Electron 应用的守护进程。
这是两个问题。第一个问题就是包太大的问题,这个其实是挺难解决的,在立项之前就应该跟团队负责人沟通好需求,是要快速的业务开发,还是更高的性能,更小的体积?如果要更小的体积,那么就放弃业务开发效率。可以使用 C++ 做一些工作,只是会消耗更长的时间和更多的资源。在打包的时候,要先确认是否用的是 LZMA 压缩格式,这种格式可以压缩得更小一点,但是小不了多少,最低可能还是 60 多兆。
至于怎么做守护进程,这个问题现在有两种办法,一种是写一个独立的应用,在 Electron 启动的时候应用就可以随之启动,然后它守护 Electron 进程。如果 Electron 进程崩溃的时候,就由它来启动 Electron 进程。
另一种方法是分析应用为什么崩溃,是主进程崩溃了,还是渲染的时候崩溃了,你如果主进程崩溃了,尽量还是要找到其崩溃的原因,因为一旦主进程崩溃,那么所有渲染都会失败;如果是渲染进程崩溃了,那么可以在主进程中打开一个其他的进程,来保证将渲染崩溃的通知发送给用户,由用户重启渲染进程,或者自动重启。