
簡介
Wails 是一個簡化使用 Go 編寫跨平台桌面應用程式的專案。它的前端使用原生 webview 元件(而非嵌入式瀏覽器),將全球最受歡迎的 UI 系統之強大能力帶入 Go,同時保持輕量。
2版本於2022年9月22日發布,帶來許多改進,包括:
- 運用熱門的 Vite 專案進行即時開發
- 用於管理視窗及建立選單的豐富功能
- Microsoft 的 WebView2 元件
- 產生與 Go 結構相對應的 TypeScript 模型
- 建立 NSIS 安裝程式
- 混淆建置
目前,Wails v2 提供了強大的工具,可用來建立功能豐富的跨平台桌面應用程式。
這篇部落格文章旨在探討專案目前的進展,以及未來有哪些可改進之處。
目前進展如何?
自 v2 發布以來,Wails 的普及程度持續提升,令人振奮。社群的創意及運用它打造出的精彩成果,總是不斷令我驚豔。隨著知名度提升,關注專案的人也變多了,功能要求和錯誤回報也隨之增加。
隨著時間推移,我已找出這個專案目前面臨的一些最迫切問題,也找出了一些阻礙專案發展的因素。
目前的問題
我認為以下幾個方面正在阻礙專案發展:
- API
- 繫結產生
- 建置系統
API
目前,用於建置 Wails 應用程式的 API 由 2 個部分組成:
- Application API
- Runtime API
眾所周知,Application API 只有 1 個函式:Run()。它接受大量選項,用以控制應用程式的運作方式。雖然使用起來非常簡單,但限制也很大。這是一種「宣告式」方法,隱藏了許多底層複雜性。例如,它沒有主視窗的控制代碼,因此無法直接與主視窗互動;若要這麼做,就必須使用 Runtime API。當你開始想執行建立多個視窗之類較複雜的操作時,這就會成為問題。
Runtime API 為開發人員提供許多公用函式,包括:
- 視窗管理
- 對話方塊
- 選單
- 事件
- 記錄
Runtime API 有幾個地方讓我不太滿意。首先,它要求到處傳遞「context」。這不僅令人困擾,也會讓傳入 context 後卻遇到執行階段錯誤的新手開發人員感到困惑。
Runtime API 最大的問題是,它原本是為僅使用單一視窗的應用程式而設計。隨著時間推移,對多視窗的需求日益增加,而這套 API 並不適合這種用途。
對 v3 API 的構想
如果能像這樣做,不是很棒嗎?
func main() {
app := wails.NewApplication(options.App{})
myWindow := app.NewWindow(options.Window{})
myWindow.SetTitle("My Window")
myWindow.On(events.Window.Close, func() {
app.Quit()
})
app.Run()
}這種程式化方法直覺得多,並讓開發人員能直接與應用程式元素互動。目前所有用於視窗的 runtime 方法都可以直接成為視窗物件的方法。至於其他 runtime 方法,則可以像這樣移到應用程式物件:
app := wails.NewApplication(options.App{})
app.NewInfoDialog(options.InfoDialog{})
app.Log.Info("Hello World")這是一套強大得多的 API,可用來建置更複雜的應用程式。它也能建立多個視窗,而這是GitHub 上獲得最多贊成票的功能:
func main() {
app := wails.NewApplication(options.App{})
myWindow := app.NewWindow(options.Window{})
myWindow.SetTitle("My Window")
myWindow.On(events.Window.Close, func() {
app.Quit()
})
myWindow2 := app.NewWindow(options.Window{})
myWindow2.SetTitle("My Window 2")
myWindow2.On(events.Window.Close, func() {
app.Quit()
})
app.Run()
}繫結產生
Wails 的主要功能之一,是為 Go 方法產生繫結,讓你能從 JavaScript 呼叫這些方法。目前的做法有點像權宜之計:先使用特殊旗標建置應用程式,再執行產生的二進位檔;該二進位檔會利用反射判斷繫結了哪些項目。這造成了類似先有雞還是先有蛋的困境:沒有繫結就無法建置應用程式,而不建置應用程式就無法產生繫結。這個問題有許多規避方式,但最好的做法是完全不要採用這種方法。
我們曾多次嘗試為 Wails 專案編寫靜態分析器,但都沒有太大進展。近來,隨著這方面可用的資料增加,實作起來已稍微容易一些。
與反射相比,AST 方法快得多,但也複雜得多。一開始,我們可能需要對如何在程式碼中指定繫結施加某些限制。目標是先支援最常見的使用案例,之後再加以擴充。
建置系統
與 API 的宣告式方法一樣,建置系統的設計目的也是隱藏建置桌面應用程式的複雜性。執行 wails build 時,它會在背後執行許多工作:
-
建置用於繫結的後端二進位檔並產生繫結
-
安裝前端相依套件
-
建置前端資產
-
判斷是否有應用程式圖示,若有則將其嵌入
-
建置最終二進位檔
-
如果建置目標是
darwin/universal,便會建置 2 個二進位檔,一個用於darwin/amd64,另一個用於darwin/arm64,再使用lipo建立通用二進位檔 -
如果需要壓縮,則使用 UPX 壓縮二進位檔
-
判斷是否要封裝此二進位檔;若要封裝,則:
- 確保圖示和應用程式資訊清單已編譯至二進位檔中(Windows)
- 建置應用程式套件、產生並複製圖示套件,並將二進位檔和 Info.plist 複製到應用程式套件中(Mac)
-
如果需要 NSIS 安裝程式,則進行建置
這整個流程雖然功能非常強大,卻也非常不透明。它很難自訂,也很難偵錯。
為了解決 v3 中的這個問題,我希望改用存在於 Wails 外部的建置系統。使用 Task 一段時間後,我非常喜歡它。它是設定建置系統的絕佳工具,任何使用過 Makefile 的人應該都會覺得相當熟悉。
建置系統將使用 Taskfile.yml 檔案進行設定;任何受支援的範本都會預設產生此檔案。它將包含執行目前所有工作所需的步驟,例如建置應用程式或將其封裝,讓自訂變得更容易。
這項工具不需要任何外部相依項目,因為它將成為 Wails CLI 的一部分。這表示你仍然可以使用 wails build,而它會完成目前所做的一切。不過,如果你想自訂建置流程,可以編輯 Taskfile.yml 檔案。這也表示你可以輕鬆了解建置步驟,並視需要使用自己的建置系統。
建置流程中還缺少一塊拼圖:圖示產生、壓縮和封裝等不可再分的作業。如果要求開發人員安裝一堆外部工具,使用體驗不會很好。為了解決這個問題,Wails CLI 將在 CLI 中提供所有這些功能。這表示不需要額外的外部工具,建置仍會如預期運作;但你也可以使用任何喜歡的工具取代建置流程中的任何步驟。
這將是一套透明得多的建置系統,不僅更容易自訂,也能解決許多相關的既有問題。
成果
這些正向變革將為專案帶來巨大的效益:
- 新的 API 將直覺得多,並能用來建置更複雜的應用程式。
- 使用靜態分析產生繫結將快得多,並能大幅降低目前流程的複雜度。
- 使用成熟的外部建置系統將使建置流程完全透明,並提供強大的自訂能力。
專案維護者將獲得以下效益:
- 新的 API 將更容易維護,也更容易因應新功能和新平台進行調整。
- 新的建置系統將更容易維護和擴充。我希望這能催生由社群推動的全新建置管線生態系。
- 專案內部的關注點將獲得更妥善的分離,讓新增功能和平台變得更容易。
計畫
這方面的許多實驗工作都已經完成,而且成果看起來不錯。目前這項工作還沒有明確時程,但我希望能在 2023 第一季結束前推出 Mac Alpha 版本,讓社群測試、實驗並提供意見回饋。
總結
- v2 API 採宣告式設計,對開發人員隱藏了許多細節,也不適合多視窗等功能。我們將建立一套更簡潔、更直覺且功能更強大的新 API。
- 目前的建置系統不透明且難以自訂,因此我們將改用外部建置系統,讓整個流程全面開放。
- 目前的繫結產生流程緩慢且複雜,因此我們將改用靜態分析,以消除現行方法中的許多複雜性。
我們已經為 v2 的核心投入大量心力,目前它相當穩固。現在該著手改善其上層,為開發人員提供好得多的使用體驗。
希望你和我一樣對此感到興奮。我很期待聽到你的想法和意見回饋。
敬上,
‐ Lea
附註:如果你或你的公司認為 Wails 很實用,請考慮贊助本專案。謝謝!
再附註:沒錯,那確實是使用 Wails 建置的多視窗應用程式螢幕截圖,不是示意圖。它是真的。它棒極了。即將推出。