Tauri:下一代桌面應用開發框架?
智能學習前端團隊 自創立以來,團隊專注於打破大衆對教育的刻板印象,突破固有的教學思維,攻破各類教學屏障。旨在爲每一位學生制定最合適的學習方案,予以因材施教,使優質教育隨 " 觸 " 可達。
桌面應用開發
在處於移動互聯網的當下,雖然桌面應用的重要性已經不能同往日而語,但在我們平常的日常工作和生活中,還是扮演着非常重要的角色和地位。在我們的日常工作中,離不開 Lark、VSCode 等桌面應用。
相比較於移動端而言,桌面端應用的生態多種多樣,因此也誕生了各種各樣的桌面應用開發技術棧。本次分享將會對相關常用的一些桌面應用開發框架進行介紹和分析,同時對當下比較流行(GitHub 50k star)的跨平臺桌面應用開發框架 Tauri 進行介紹。
原生技術棧
原生技術棧是指通過操作系統相關 API 或者操作系統廠家(如 Apple/Microsoft)提供的 SDK / 工具來開發桌面應用的方式。使用原生技術開發的應用,通常能夠在性能、體積以及系統的交互等方面做到非常不錯的效果。
-
優點
-
構建產物體積小
-
性能好
-
系統 API 調用方便
-
兼容性好
-
和系統應用的交互融合度高,如要實現如下的一些系統原生 UI 組件非常方便
-
缺點
-
無法做到跨平臺,所開發的應用只能在對應的平臺上運行,如果需要跨平臺運行,則需要在不同的操作系統上分別開發,開發成本高
-
對於使用的技術棧有限制(Windows 使用 C#,macOS 使用 ObjC/Swift)
Windows 平臺
作爲目前使用率最高的操作系統,Windows 平臺的 GUI 程序開發經歷了漫長的迭代和演化的過程:Win32 API 作爲 Windows GUI 開發的鼻祖,通過 C 語言調用 Windows 底層的繪圖函數來進行開發;在 Win32 API 之後出現了 MFC(Microsoft Fundation Class),MFC 通過 C++ 語法將原有的 Win32 API 封裝成了控件類(對話框控件、按鈕控件等);在 MFC 之後,微軟推出了 Windows Form(2002 年),Windows Form 依賴於. NET 的運行時,提供了組件化的開發能力;在此之後,微軟推出了 WPF(Windows Presentation Fundation,2006 年),WPF 提供了基於 XML 的語言 XAML 來描述 UI;在 Windows8 的時代,微軟又推出了 UWP(Universal Windows Platform,2015 年),UWP 支持在各種平臺上運行(PC/Windows Phone/Xbox),API 也支持多種語言(C++/VB/C#/JS)。
從 Windows 平臺應用的開發技術迭代來看,也可以大致看出 GUI 程序的技術發展史:
-
Win32API 時代:函數調用,指令式,Windows 系統處理
-
MFC 時代:面向對象,把一些指令式調用封裝成類,由來自 UI 的消息驅動程序處理數據
-
Windows Form 時代:組件化,在類的基礎上封裝成組件,消息被封裝成事件,事件驅動
-
WPF 時代:使用類 XML 語言來描述 UI,引入數據驅動 UI 的理念
-
UWP 時代:跨平臺、多語言
macOS 平臺
現有的 macOS 原生應用主要基於 Cocoa 框架開發,Cocoa 是從 1980 年代由 NeXT(macOS 的前身)開發的編程環境 NeXTSTEP 和 OPENSTEP 演變而來,是面向對象的 API。
在 2020 年的 WWDC 上,蘋果推出了新一代的 UI 框架 SwiftUI,和 Flutter/React 等現代 GUI 框架類似,支持聲明式的方式使用 Swift 語言作爲 DSL 來編寫 UI,同時也支持跨平臺的特性,可以在 macOS/iOS/tvOS 等多平臺運行。
Linux 平臺
Linux 其源碼只包含了操作系統內核的部分,桌面並不屬於 Linux 源碼的一部分,因此嚴格意義上來說,從「使用系統 API 和操作系統廠商提供的 SDK 開發的應用爲原生應用」的定義上來說,並無所謂「原生技術棧」的概念。我們日常使用的發行版提供了桌面環境如 KDE、Gnome 等,Linux 發行版的這些桌面環境也提供了相關的一些庫或者 API 來幫助繪製 GUI 程序,如 gtk + 等,可以認爲是「原生技術棧」。
跨平臺技術棧
Web 技術棧
Atwood's Law: Any application that can be written in JavaScript, will eventually be written in JavaScript.
一個你或許不知道的冷知識,macOS 的系統設置頁面是 Webview+React 寫的 [1]
提到跨平臺,就不得不提 Web 生態了,Web 相關的技術在跨平臺中永遠是最受青睞的選擇,無論是開發的便捷程度,還是龐大的 JS 開發者生態等等因素,都使得 Web 技術無論是在移動端還是桌面端的跨平臺應用開發上都穩坐使用率最高的技術棧。
-
優點
-
開發成本比原生低,可以方便做到一套代碼在不同操作系統上運行
-
實現複雜的 UI 和動效方便,可以更快地實現一些比較炫酷的 UI 界面
-
缺點
-
調用系統原生 API 不方便通常需要使用打包其他的運行時環境或 JSBridge 的方式來進行調用
Electron
Electron(原名爲 Atom Shell)是 GitHub 開發的一個開源框架,最初用來開發 Atom 編輯器。它通過使用 Node.js(作爲後端)和 Chromium 的渲染引擎(作爲前端)完成跨平臺的桌面 GUI 應用程序的開發。
-
代表應用:VSCode(303M)、Figma(213M)、Bilibili(397M)、Discord(367M)、QQ Beta(747M)、1Password8(343M)、MS Teams(264M,根據參考文獻 [2],微軟正在替換 Electron 的實現,但目前看我電腦中下載的版本解包中,依然還有 Electron.framework 的文件)
-
優點
-
開發方便,技術棧適合前端同學(UI 使用 Web 技術,系統 API 交互部分使用 NodeJS)
-
缺點
-
打包體積大,需要打包 Chromium 和 NodeJS 的運行時環境
-
內存消耗大:Chromium 本身比較喫內存,同時 NodeJS 是 JIT 運行的,相比較 C++ 等 AOT 的語言來說內存消耗也更大。
- 性能需要多花點時間優化
實際上,並不代表 Electron 技術開發的應用性能就一定不如其他技術棧,總的來說,具體的性能表現還是取決於開發者的投入,例如微軟在 VSCode 的博客中給到了一個例子,能夠將 VSCode 在渲染括號顏色匹配的速度提高 10000 倍 [3]。
CEF(Chromium Embedded Framework)
由於需要將 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 程序,在工業、嵌入式等領域的桌面程序中有着非常深入的使用。
-
代表應用:WPS Office、剪映桌面版、AutoDesk
-
優點
-
性能好,與 Native 開發的應用性能相差無幾
-
支持的操作系統豐富,跨平臺性能好
-
缺點
-
C++ 開發成本高
-
GPL 協議,商業版本需要給錢,不符合咱們去肥增瘦的理念
Flutter(Dart)
Flutter 是一個由 Google 開發的跨平臺應用開發框架,最初只用於移動端爲 Android、iOS 開發應用。2015 年 4 月,Flutter 正式發佈,其目標是希望可以在跨平臺的特性上,實現 120FPS 的渲染性能。2018 年,Flutter 1.0 發佈,是該框架的第一個穩定版本。2022 年 5 月,Google 在 Google I/O 2022 發佈了 Flutter 3.0 版本,宣佈對 Windows、macOS、Linux 桌面操作系統提供支持。
-
代表應用:?(Flutter 在 2022 年 5 月份發佈 3.0 版本,此時桌面應用才進入正式版支持,目前還並不成熟,所以在線上使用的較少,暫時沒聽過有啥桌面應用是用 Flutter 寫的)
-
優點
-
性能好(相比較 Web 技術棧)
-
Dart 語言容易學習和上手、開發成本低
-
缺點
-
桌面端纔剛剛發佈穩定版支持,生態和穩定性都有待考量
Swing(Java)
Swing 是一個用於開發 Java GUI 應用的框架。它採用純 Java 實現,不再依賴於本地平臺的圖形界面,所以可以在所有平臺上保持相同的運行效果,對跨平臺支持比較出色。
-
代表應用:Jetbrains IDE
-
優點
-
跨平臺性能好:write once run anywhere (write once debug everywhere)
-
缺點
-
需要打包 JRE,體積大
總的來說,雖然不同大類技術棧的應用具體實現原理有所不同,但是相關開發的技術棧的大致特點可以歸納如下:
-
系統 API 調用和交互:原生應用 > 自渲染應用 > Web 應用
-
開發便捷程度:Web 應用 >> 自渲染應用 > 原生應用
-
打包體積:Web 應用 > 自渲染應用 > 原生應用
-
性能:原生應用 > 自渲染應用 > Web 應用
Tauri 介紹
從上面的介紹可以看出,不同的技術棧的實現原理和特點各有區別,但是很難做到開發便捷程度、UI 複雜效果、打包體積和性能等多個方面的兼顧,只能是根據應用的類型和具體的業務場景去決定到底使用哪種框架。
所以有沒有一種開發方式,可以在性能、體積、開發等多個角度上,取得一個比較好的平衡呢?這就來到了我們今天需要介紹的桌面應用開發框架 Tauri。
Build an optimized, secure, and frontend-independent application for multi-platform deployment.
從上面 Tauri 官網的宣傳語可以看出 Tauri 主打的幾個賣點 [4]:
-
optimized:性能高、體積小
-
secure:安全性強
-
frontend-independent:前端獨立
-
multiplatform:跨平臺
那麼 Tauri 是如何通過在框架層面的設計來保證這樣的一些特性的呢?我們一起接着往下看⬇️
Rust
Tauri 框架是由 Rust 語言實現的,同時 Tauri 應用的後端也是由 Rust 來編寫的。Rust 是由 Mozilla 主導開發的通用、編譯型的系統編程語言。設計準則爲 “安全、併發、實用”,支持函數式、併發式、過程式以及面向對象的編程風格。[5] 相比較其他語言,Rust 有如下的一些特性:
-
性能高(optimized):Rust 的性能和 C/C++ 的性能不相上下,由於 Rust 的「所有權」機制,Rust 不需要 GC,同時也能避免如 C/C++ 之類需要手動管理內存的語言忘記釋放內存導致的內存泄露的問題;
-
安全性強(secure):Rust 設計了一個所有權系統,其中所有值都有一個唯一的所有者,並且值的作用域與所有者的作用域相同。值可以通過不可變引用(&T)、可變引用(&mut T)或者通過值本身(T)傳遞。任何時候,一個變量都可以有多個不可變引用或一個可變引用,這實際上是一個顯式的讀寫鎖。Rust 編譯器在編譯時強制執行這些規則,並檢查所有引用是否有效。能夠有效避免 C/C++ 等語言中的懸垂指針等問題;
-
FFI 編譯友好(multiplatform):FFI 是可以用一種編程語言寫的程序能調用另一種編程語言寫的代碼的機制,使用 Rust 可以方便地提供接口給其他語言調用;
WRY[1]:Webview Render Library
由於 Web 技術的表現力強、開發成本低的特點,與 Electron、CEF 等框架類似,Tauri 應用的前端實現也是使用 Web 技術棧編寫的。那麼 Tauri 是如何解決 Electron/CEF 等框架遇到的 Chromium 內核體積過大的問題呢?
大家可能會想,如果每一個應用都需要把瀏覽器內核打包進去才能實現 Web 頁面的渲染,要是所有的應用都共享同一個內核就好了,這樣我們在分發應用的時候,不需要打包瀏覽器內核,只需要打包 Web 頁面的資源不就好了嗎?所以 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:展示系統文件選擇、文件保存彈窗
-
event:給後端發出一些事件,後端監聽並處理
-
fs:文件系統的操作,提供文件讀寫等能力
-
globalShortcut:註冊全局快捷鍵
-
http:使用 Rust 的 Http 客戶端進行網絡請求
-
notification:系統通知
-
os:獲取操作系統的一些信息
-
path:文件和文件夾路徑處理的一些工具
-
process:對當前的進程進行一些操作
-
shell:對系統 shell 的一些操作
需要注意的是,上述的一些 API,爲了保證安全性,對於權限都有着嚴格的限制,都是默認關閉的狀態,需要修改配置以手動啓用相關的功能。
進程模型
和 Electron 類似,Tauri 也採用的是多進程的架構(Electron 中有主進程和渲染進程),多進程的好處是能夠更好更有效地利用現代多核 CPU 的能力,同時一個組件的崩潰也不會影響到其他組件的運行,因爲組件被隔離在了不同的進程中。如果應用中的某個進程崩潰了,我們只要重啓該進程即可。還可以通過只給每個進程分配足夠完成工作的最低限度的權限,來限制潛在漏洞的破壞範圍。這種模式被稱爲最小權限原則。
在 Tauri 中,進程分爲兩類:主進程和 Webview 進程。每個 Tauri 應用程序都有一個主進程,它作爲應用程序的入口點,是唯一可以完整訪問操作系統的組件。主進程的主要職責是使用訪問權限來創建和管理應用程序窗口、系統托盤菜單或通知。Tauri 實現了必要的跨平臺抽象來簡化該操作。它還通過核心進程路由所有的 IPC,通過類似於事件總線的機制,可以方便地攔截、過濾和操作 IPC 消息。主進程還應該負責管理全局狀態,例如數據庫連接。這使你能夠輕鬆地在窗口間同步狀態,並保護你的業務敏感數據。主進程自身並不渲染實際的用戶界面,它會直接利用操作系統提供的 WebView 庫來實現頁面渲染,不同的窗口之間會擁有不同的 WebView 進程,WebView 進程用來負責渲染對應的 UI。
IPC 模式
Brownfield 模式(默認)
Brownfield 軟件開發是指在現有或遺留軟件系統存在的情況下開發和部署新的軟件系統。[6]
使用 Tauri 的最簡單和直接的模式,因爲 Brownfield 模式會盡最大可能嘗試與現有的前端項目兼容。在這種模式下,無需現有的瀏覽器前端項目進行任何操作即可遷移。
隔離模式
隔離模式是一種在到達 Tauri 主進程前,攔截並修改由 Webview 進程發送的 Tauri API 信息的 IPC 模式,其完全使用 JS 編寫。由隔離模式保障的 JS 代碼即稱爲隔離應用。隔離模式的目的是爲開發者提供一種保護機制,防止其應用程序被預料之外或惡意的 Webview 進程調用 Tauri 主進程。隔離模式的需求來自於前端中不可信任的內容所帶來的威脅,常見於需要許多依賴的應用。隔離模式在設計之初時設想的最大威脅爲開發威脅,因爲前端構建工具不僅僅由許許多多嵌套很深的依賴組成,而且還有很多嵌套很深的依賴被打包到最終的網頁構建產物中。
-
原理:隔離模式就是在 Webview 進程和 Tauri 主進程之間注入一個安全的應用程序,用以攔截和修改傳入的 IPC 信息。它使用
<iframe>
的沙盒特性,與 Webview 進程一起安全地執行 JS 代碼。Tauri 在加載頁面時會強制執行隔離模式,使所有對 Tauri 主進程的 IPC 調用必須先通過沙盒隔離應用程序。當消息準備被傳遞給 Tauri 主進程時,其就會被使用瀏覽器的 SubtleCrypto API[5] 實現加密,並傳遞迴主前端程序,之後,它將會被直接傳遞給 Tauri 主進程來解密和讀取數據。 -
步驟:
-
缺點
-
由於信息經過加密,所以隔離模式相比 Brownfield 模式而言存在額外開銷。除去性能敏感的應用 (使用很少依賴來提升性能的應用) 之外,使用 AES-GCM 算法來加解密 IPC 信息會對大部分應用造成相對較小的性能影響。
-
Windows 平臺上,Webview 限制了因爲沙盒環境下加載
<iframe>
標籤內的外部文件,Tauri 在構建時實現一些步驟將腳本注入,但是 ES Modules 可能無法正常加載。
安全性
-
動態 AOT:Tauri 應用啓動時將多次進行編譯。通過使用 Tauri 提供的動態預編譯器,可以生成每個會話都不同的代碼引用,但技術上一致的代碼單元;
-
函數 ASLR:函數地址空間佈局隨機化 (Address Space Layout Randomization) 將在運行時隨機調整函數名稱,且可以實現 OTP 哈希功能,這樣將永遠不會出現相同的會話。在 Tauri 應用啓動時,或可選在每次執行後,隨機生成函數名稱。每個函數指針均使用 UID 以防止靜態攻擊;
-
自殺式函數注入:一種高級的 ASLR 技術,Rust 在運行時載入進入 Webview 的閉包 Promise 和隨機的句柄,API 接口在 Promise 處理中就被鎖定,在執行完畢後被立即設置成 null;
可以有效防止惡意頁面被加載
-
使用 Event Bridge:Tauri 內部的信息通信使用 Event Bridge,Event Bridge 用來保證只能傳遞信息和指令給一個指定的中間代理,而不是直接傳遞不安全的函數調用;
-
API Allow List:Tuari 提供的 API 都是關閉狀態,若沒有啓用相關 API,應用構建時不會包括相關功能函數的代碼。這可以減少二進制文件大小及攻擊面。同時 API 還有嚴格的權限選項,如文件讀寫相關 API 可以設置只讀 / 只寫 / 指定目錄或文件等功能;
-
CSP:Tauri 會對本地的 HTML 頁面強制使用內容安全策略,本地腳本經過哈希處理,同時樣式、外部腳本經由加密隨機字符串引用,防止禁止內容被加載;
-
反編譯難:Tauri 在構建時,會將相關前端代碼直接構建在二進制可執行文件中,這意味着和 Electron 的 ASAR 文件不同,具體的代碼無法被輕鬆地反編譯;
-
一次性 Token 和哈希:使用 OTP 加鹽哈希處理重要的信息,可以在前端和 Rust 後端之間加密信息;
Tauri-egui[6]
因爲 Tauri 應用的前端是用了 Web 相關的技術棧,因此在運行時總有辦法來使用開發者模式 / 調試工具等來進行檢查元素等。在某些情況下,如密碼輸入等場景,我們希望能夠保證前端的 UI 展示是無法被修改的,這時可以使用 Tauri-egui 這個庫,這個庫對 egui 進行了封裝,可以使用 Rust 來編寫前端的 UI。
優點
Benchmark
- 內存使用
- 構建產物體積
缺點
- 兼容性:由於 Tauri 使用的是系統 Webview,因此在構建時前端的代碼需要做 polyfill;且 Windows 平臺上,由於 Webview2 只在 Windows10/11 上有默認推送安裝,要想在 Windows7/8 等較低版本的平臺上運行的話,還需要另外安裝 Webview2 的運行時。
參考資料
[1] 在 macOS 中打開系統 Webview 的檢查元素開關, https://blog.jim-nielsen.com/2022/inspecting-web-views-in-macos/
[2] 微軟開始在 Teams 應用中放棄 Electron, https://blog.devgenius.io/microsoft-is-finally-ditching-electron-9e081757f9db
[3] VSCode 優化括號顏色匹配, https://code.visualstudio.com/blogs/2021/09/29/bracket-pair-colorization
[4] Tauri 官網, https://tauri.app
[5] Rust Wikipedia, https://zh.wikipedia.org/zh-cn/Rust
[6] Brownfield, https://en.wikipedia.org/wiki/Brownfield_(software_development)
智能學習前端團隊 自創立以來,團隊專注於打破大衆對教育的刻板印象,突破固有的教學思維,攻破各類教學屏障。旨在爲每一位學生制定最合適的學習方案,予以因材施教,使優質教育隨 " 觸 " 可達。
參考資料
[1]
WRY: https://github.com/tauri-apps/wry
[2]
WebKit.WKWebview: https://developer.apple.com/documentation/webkit
[3]
Webview2: https://developer.microsoft.com/zh-cn/microsoft-edge/webview2/
[4]
WebKitGTK: https://webkitgtk.org/
[5]
SubtleCrypto API: https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto
[6]
Tauri-egui: https://github.com/tauri-apps/tauri-egui
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/G4K3nIB1SsYDsOxMv7CyFg