桌面应用开发在移动互联网时代,虽然桌面应用的重要性已不复当年,但在我们日常的工作和生活中仍然扮演着非常重要的角色和地位。在我们的日常工作中,离不开Lark、VSCode等桌面应用。相对于移动端,桌面应用生态是多样的,因此也诞生了各种桌面应用开发技术栈。本次分享将对一些常用的桌面应用开发框架进行介绍和分析,并介绍目前流行的跨平台桌面应用开发框架Tauri(GitHub50kstars)。原生技术栈原生技术栈是指通过操作系统厂商(如Apple/Microsoft)提供的操作系统相关API或SDK/工具来开发桌面应用程序的方式。使用原生技术开发的应用程序通常可以在性能、大小和系统交互方面取得非常好的结果。优点:体积小,性能好,系统API调用方便,兼容性好,系统应用的交互性和集成度高。实现以下系统原生UI组件非常方便。缺点,不能跨平台。如果需要跨平台运行,需要在不同的操作系统上分别开发。高昂的开发成本对使用的技术栈有限制(Windows使用C#,macOS使用ObjC/Swift)。Windows平台是目前使用最多的操作系统,Windows平台GUI程序开发经历了一个漫长的迭代和演化过程:Win32API作为WindowsGUI开发的鼻祖,使用C语言调用Windows的底层绘图函数来实现发展;在Win32API、MFC(MicrosoftFoundationClass)之后,MFC通过C++语法将原有的Win32API封装成控件类(对话框控件、按钮控件等);在MFC之后,微软推出了WindowsForm(2002年),WindowsForm依靠.NET运行时提供了基于组件的开发能力;之后,微软推出了WPF(WindowsPresentationFoundation,2006),提供了一种基于XML的语言XAML来描述UI;Windows8时代,微软推出了UWP(UniversalWindowsPlatform,2015),UWP支持运行在各种平台(PC/WindowsPhone/Xbox),API也支持多种语言(C++/VB/C#/JS).从Windows平台应用的开发技术迭代来看,我们也可以大致看到GUI程序的技术发展史:Win32API时代:函数调用,命令式,Windows系统处理MFC时代:面向对象,将一些命令式调用封装成类,数据由UIWindowsForm时代的消息驱动处理:组件化,封装成基于类的组件,消息封装成事件,事件驱动的WPF时代:使用类似XML的语言描述UI,引入数据驱动UIUWP时代概念:跨平台、多语言macOS平台现有的macOS原生应用主要基于Cocoa框架开发。Cocoa由NeXT(macOS的前身)在1980年代开发的编程环境NeXTSTEP和OPENSTEP演变而来,是一种面向对象的API。.在2020年的WWDC上,Apple推出了新一代的UI框架SwiftUI,它类似于Flutter/React等现代GUI框架。它支持以声明方式使用Swift语言作为DSL来编写UI,同时也支持跨平台特性。在macOS/iOS/tvOS等多个平台上运行。Linux平台Linux的源代码只包括操作系统的内核,桌面不属于Linux源代码的一部分。因此,严格来说,从“使用系统API和操作系统厂商提供的SDK开发的应用程序是原生应用程序”的定义来看,一般来说并没有“原生技术栈”这个概念。我们日常使用的发行版都提供了KDE、Gnome等桌面环境,Linux发行版的这些桌面环境也提供了一些相关的库或者API来帮助绘制GUI程序,比如gtk+等,可以认为是“原生技术”栈”。.跨平台技术栈Web技术栈阿特伍德定律:任何可以用JavaScript编写的应用程序,最终都将用JavaScript编写。一个你可能不知道的冷知识,macOS的系统设置页面是Webview+React[1]写的,说到跨平台,就不得不提到Web生态。与Web相关的技术始终是跨平台最流行的选择。无论是开发的便捷性,还是庞大的JS开发者生态等等,种种因素都使得Web技术无论是移动还是桌面跨平台应用开发,都是使用率最高的技术栈。优点开发成本比原来低,方便一套代码在不同操作系统上运行,实现复杂的UI和动态效果。方便更快的实现一些炫酷的UI界面。缺点调用系统原生API不方便。通常需要使用封装其他运行环境或JSBridge的方式来调用ElectronElectron(原名AtomShell)是GitHub开发的一个开源框架,最初用于开发Atom编辑器。它使用Node.js(作为后端)和Chromium的渲染引擎(作为前端)完成跨平台桌面GUI应用程序的开发。代表应用:VSCode(303M)、Figma(213M)、Bilibili(397M)、Discord(367M)、QQBeta(747M)、1Password8(343M)、MSTeams(264M,据参考文献[2],微软正在更换Electron不过我电脑下载的解压版里面还是有Electron.framework文件的)优点是容易开发,技术栈适合前端同学(UI采用Web技术,系统API交互部分采用NodeJS)缺点打包体积大,需要打包Chromium和NodeJS的运行环境消耗大量内存:Chromium本身消耗内存较多,而NodeJS是JIT运行的。与C++等AOT语言相比,内存消耗也更大。优化性能需要更多时间。其实并不是说Electron技术开发的应用性能就一定不如其他技术栈。一般来说,具体表现取决于开发商的投资。给出了一个例子,可以将VSCode渲染括号颜色匹配的速度提高10000倍[3]。CEF(ChromiumEmbeddedFramework)需要封装Chromium和NodeJS的运行时,所以Electron构建的应用体积会非常大,但是CEF的存在解决了Electron的这个问题(其实CEF的出现比Electron早很多向上)。由于Chromium中有很多第三方组件(如ffmpeg等),我们在开发应用的过程中通常不会用到Chromium的全部能力,所以CEF提供了一个轻量级的嵌入式Chromium,也可以根据需要定制根据需要自己剪裁。CEF提供了将Chromium嵌入到应用程序中显示Webview的能力,同时也提供了一些C++的API。当你需要做一些浏览器无法实现的原生API依赖的功能时(比如读写系统文件等),你需要使用C++(或者其他语言,但CEF的原生接口是C++)来编写相关能力,并提供JSBridge供前端代码调用。代表应用:网易云音乐、Spotify、飞书等自渲染技术栈想要实现跨平台的GUI应用。更流行的方法是实现自渲染管道。上层提供类Canvas的绘图、渲染和排版能力,下层使用OpenGL/Vulkan/Metal等图形API进行绘图。在Web跨平台桌面应用开发技术栈出现之前,很多应用开发框架都是采用类似的思路来实现跨平台应用开发,比如QT(C++语言)、Flutter(Dart语言,基于Skia渲染)和Swing(Java语言)等。相比Electron和CEF的解决方案,由于不需要封装运行环境(Swing除外,JRE需要封装),减少了Bridge转换,体积和运行效率通常更好比Web技术栈。优点自绘性能通常优于Web跨平台技术(取决于框架实现)缺点开发成本略高于Web技术栈实现复杂效果的能力不如原生和Web技术栈,通常需要编写更复杂的代码(取决于具体框架的设计,Flutter做得更好)Qt(C++)Qt(/?kju?t/)是一个跨平台的C++应用程序开发框架,广泛用于GUI程序的开发,在工业,嵌入式等领域的桌面程序中使用非常深入。代表应用:WPSOffice、Clipping桌面版、AutoDesk优点性能好,与Native应用性能相差无几支持的操作系统丰富,跨平台性能好缺点C++开发成本高GPL协议,商业版需要付费,不支持满足我们肥肥瘦身的想法Flutter(Dart)Flutter是谷歌开发的跨平台应用开发框架。最初只是用来开发移动端Android和iOS的应用。2015年4月,Flutter正式发布,目标是在跨平台特性的基础上实现120FPS的渲染性能。2018年,Flutter1.0发布,这是该框架的第一个稳定版本。2022年5月,谷歌在GoogleI/O2022上发布了Flutter3.0版本,宣布支持Windows、macOS和Linux桌面操作系统。代表应用:?(Flutter会在2022年5月发布3.0版本,这时候桌面应用会进入正式版支持,还不成熟,所以网上用的很少,没听说过用Flutter写的桌面应用。)优点性能好(相对于Web技术栈)Dart语言易学易用,开发成本低缺点桌面刚刚发布稳定版支持,生态和稳定性不得不考虑Swing(Java)Swing是一个JavaGUI应用开发框架。纯Java实现,不再依赖本地平台的图形界面,因此在所有平台上都能保持相同的运行效果,具有极好的跨平台支持。代表应用:JetbrainsIDE优点跨平台性能好:writeoncerunanywhere(writeoncedebugeverywhere)缺点JRE需要打包,体积大开发的技术栈的一般特点可以概括为:SystemAPI调用与交互:原生应用>自渲染应用>Web应用开发便利性:Web应用>自渲染应用>原生应用打包体积:Web应用>自渲染应用>原生应用性能:原生应用>自渲染应用>Web应用Tauri介绍从上面的介绍我们可以看出,不同的技术栈实现原理和特点不同,但很难做到开发方便、UI效果复杂、封装大小、性能等多方面考虑,我们只能决定使用哪个框架基于应用类型和具体业务场景。那么有没有一种开发方式,可以在性能、体积、开发等方面取得更好的平衡呢?这就把我们带到了今天要介绍的桌面应用程序开发框架Tauri。为多平台部署构建优化、安全且独立于前端的应用程序。从上面Tauri官网的slogan,我们可以看出Tauri的几个卖点[4]:optimized:高性能,小体积secure:强前端-independent:前端独立multiplatform:跨平台那么Tauri是如何做到的呢?通过框架级别的设计来保证这些功能?我们继续往下看??RustTauri框架是用Rust语言实现的,Tauri应用的后端也是用Rust写的。Rust是由Mozilla开发的一种通用的编译系统编程语言。设计原则是“安全、并发、实用”,支持函数式、并发式、过程式、面向对象的编程风格。[5]与其他语言相比,Rust具有以下特点:高性能(优化):Rust的性能可与C/C++相媲美。由于Rust的“所有权”机制,Rust不需要GC,也可以避免C/C++等语言需要手动管理内存而忘记释放内存导致的内存泄漏问题;强安全性(secure):Rust设计了一个所有权系统,其中所有的值都有一个唯一的所有者,值与所有者有相同的作用域。值可以通过不可变引用(&T)、可变引用(&mutT)或值本身(T)传递。在任何时候,一个变量都可以有多个不可变引用或一个可变引用,这实际上是一个显式读写锁。Rust编译器在编译时强制执行这些规则并检查所有引用是否有效。可以有效避免C/C++等语言中悬垂指针等问题;FFI编译友好(多平台):FFI是一种机制,允许用一种编程语言编写的程序调用用另一种编程语言编写的代码。使用Rust可以方便WRY[1]:WebviewRenderLibrary类似于Electron、CEF等框架,由于Web技术的表现力强、开发成本低,Tauri应用的前端实现也是使用编写的Web技术栈.那么Tauri是如何解决Electron/CEF等框架遇到的Chromium内核体积过大的问题呢?你可能会想,如果每个应用程序都需要封装浏览器内核来渲染网页,那么如果所有应用程序共享同一个内核就好了,这样我们在分发应用程序时就不需要封装浏览器了不是吗内核只需要打包网页的资源就好了?所以Tauri采用了这样的方案。WRY是Tauri封装的Webview框架。封装了系统Webview在不同操作系统平台上的实现:在macOS上使用WebKit.WKWebview[2],在Windows上使用Webview2。[3],在Linux上使用WebKitGTK[4]。这样,在Tauri应用运行时,会直接使用系统Webview来渲染应用的前端显示。API接口对于不会用Rust的同学来说,学习Rust还是有很大的学习成本的,不过不用担心,需求简单的时候根本不需要写Rust代码.Tauri框架提供了以下API,可以方便地调用JS中的原生能力:cli:应用启动时解析命令行参数clipboard:读写系统剪贴板dialog:显示系统文件选择,文件保存弹出窗口事件:向后端发送一些事件,后端监听并处理fs:文件系统操作,提供文件读写能力globalShortcut:注册全局快捷键http:使用Rust的Http客户端进行网络请求notification:系统通知os:获取一些信息操作系统路径的:文件和文件夹路径处理的一些工具进程:对当前进程执行一些操作shell:对系统shell的一些操作需要注意的是,上面的一些API,为了保证安全,权限限制严格,默认全部禁用。您需要修改配置以手动启用相关功能。进程模型类似于Electron。Tauri也采用了多进程架构(Electron有一个主进程和一个渲染进程)。多进程的优势在于它可以更好、更有效地利用现代多核CPU的能力。它不会影响其他组件的运行,因为组件在不同的进程中是隔离的。如果应用程序中的某个进程崩溃了,我们只需重新启动该进程即可。您还可以通过仅向每个进程分配足以完成其工作的最低限度权限来限制潜在漏洞的范围。这种模式被称为最小特权原则。在Tauri中,进程分为两类:主进程和Webview进程。每个Tauri应用程序都有一个主进程,它作为应用程序的入口点,是唯一可以完全访问操作系统的组件。主进程的主要职责是使用访问权限创建和管理应用程序窗口、系统托盘菜单或通知。Tauri实现了必要的跨平台抽象来简化这一过程。它还通过核心进程路由所有IPC,并通过类似于事件总线的机制,可以轻松拦截、过滤和操作IPC消息。主进程还应该负责管理全局状态,例如数据库连接。这使您能够轻松地在窗口之间同步状态并保护您的业务敏感数据。主进程本身并不渲染实际的用户界面,它会直接使用操作系统提供的WebView库来实现页面渲染,不同的窗口之间会有不同的WebView进程,WebView进程用于渲染相应的用户界面。IPC模式Brownfield模式(默认)Brownfield软件开发是指在现有或遗留软件系统存在的情况下开发和部署新软件系统。[6]使用Tauri最简单直接的模式,因为Brownfield模式试图尽可能兼容现有的前端项目。在这种模式下,现有的浏览器前端项目不需要做任何迁移。隔离模式隔离模式是一种IPC模式,在到达Tauri主进程之前拦截并修改Webview进程发送的TauriAPI信息,完全用JS编写。隔离模式保证的JS代码称为隔离应用。隔离模式的目的是为开发者提供一种保护机制,以防止他们的应用程序被意外或恶意的Webview进程调用到Tauri主进程中。对隔离模式的需求来自前端不受信任内容的威胁,这在需要许多依赖项的应用程序中很常见。隔离模式在设计之初最大的威胁就是开发威胁,因为前端构建工具不仅由很多深度嵌套的依赖组成,而且很多深度嵌套的依赖被打包到最终的网页构建产品中中间。原理:隔离模式是在Webview进程和Tauri主进程之间注入一个安全的应用程序来拦截和修改传入的IPC信息。它利用
