
简介
Wails 是一个项目,它简化了使用 Go 编写跨平台桌面应用程序的能力。它使用本地 webview 组件作为前端(而不是嵌入式浏览器),将世界上最流行的 UI 系统的功能带到 Go,同时保持轻量级。
版本 2 于 2022 年 9 月 22 日发布,带来许多增强功能,包括
- 实时开发,利用流行的 Vite 项目
- 用于管理窗口和创建菜单的丰富功能
- 微软的 WebView2 组件
- 生成与 Go 结构体相匹配的 Typescript 模型
- 创建 NSIS 安装程序
- 混淆构建
目前,Wails v2 为创建丰富、跨平台的桌面应用程序提供了强大的工具。
这篇博文旨在探讨该项目目前的状况以及我们可以改进的方向。
我们现在身处何处?
看到自 v2 发布以来 Wails 的受欢迎程度不断上升,真是令人难以置信。我一直对社区的创造力以及他们使用它构建的精彩事物感到惊叹。随着知名度的提高,更多的人关注着这个项目。随之而来的是更多功能请求和错误报告。
随着时间的推移,我能够识别出该项目面临的一些最紧迫的问题。我也能够识别出阻碍项目发展的一些因素。
当前问题
我已经确定了以下我认为阻碍项目发展的一些领域
- API
- 绑定生成
- 构建系统
API
构建 Wails 应用程序的 API 目前由两部分组成
- 应用程序 API
- 运行时 API
众所周知,应用程序 API 只有一个函数:Run()
,它接受大量选项,这些选项控制应用程序的工作方式。虽然这非常易于使用,但它也有很大的局限性。它是一种“声明式”方法,隐藏了许多底层复杂性。例如,没有主窗口的句柄,因此您无法直接与它交互。为此,您需要使用运行时 API。当您想要执行更复杂的操作(如创建多个窗口)时,这就会成为一个问题。
运行时 API 为开发人员提供了许多实用函数。这包括
- 窗口管理
- 对话框
- 菜单
- 事件
- 日志
我对运行时 API 中的一些东西并不满意。首先,它需要传递一个“上下文”。这对新开发人员来说既令人沮丧又令人困惑,他们传递了一个上下文,然后出现运行时错误。
运行时 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()
}
这种编程方法直观得多,并允许开发人员直接与应用程序元素交互。所有当前用于窗口的运行时方法都将只是窗口对象上的方法。对于其他运行时方法,我们可以将它们移到应用程序对象中,如下所示
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
,则构建两个二进制文件,一个面向darwin/amd64
,另一个面向darwin/arm64
,然后使用lipo
创建一个胖二进制文件 - 如果需要压缩,则使用 UPX 压缩二进制文件
- 确定是否要打包此二进制文件,如果要打包
- 确保图标和应用程序清单被编译到二进制文件中(Windows)
- 构建应用程序包,生成图标包并将其复制、二进制文件和 Info.plist 到应用程序包中(Mac)
- 如果需要 NSIS 安装程序,则构建它
虽然这个过程非常强大,但也非常不透明。它非常难以自定义,也很难调试。
为了解决这个问题,在 v3 中,我想将构建系统迁移到 Wails 之外的构建系统。在使用了一段时间Task之后,我成为了它的忠实粉丝。它是一个用于配置构建系统的绝佳工具,应该让任何使用过 Makefiles 的人都觉得比较熟悉。
构建系统将使用Taskfile.yml
文件进行配置,该文件将默认情况下在任何支持的模板中生成。它将包含执行所有当前任务所需的所有步骤,例如构建或打包应用程序,允许轻松自定义。
对于此工具,没有外部要求,因为它将成为 Wails CLI 的一部分。这意味着您仍然可以使用wails build
,它将执行与现在相同的操作。但是,如果您想自定义构建过程,可以通过编辑Taskfile.yml
文件来实现。这也意味着您可以轻松地理解构建步骤,并且如果您愿意,可以使用自己的构建系统。
构建难题中缺失的部分是构建过程中的原子操作,例如图标生成、压缩和打包。要求使用大量的外部工具对于开发人员来说体验并不理想。为了解决这个问题,Wails CLI 将在 CLI 中提供所有这些功能。这意味着构建仍然按预期工作,无需额外的外部工具,但您可以用您喜欢的任何工具替换构建的任何步骤。
这将是一个更加透明的构建系统,它将允许更轻松地进行自定义并解决围绕构建系统提出的许多问题。
回报
这些积极的变化将为该项目带来巨大的好处
- 新的 API 将更加直观,并将允许构建更复杂的应用程序。
- 使用静态分析进行绑定生成将快得多,并减少当前过程中的许多复杂性。
- 使用成熟的外部构建系统将使构建过程完全透明,允许进行强大的自定义。
对项目维护者的益处是
- 新的 API 将更容易维护和适应新功能和平台。
- 新的构建系统将更容易维护和扩展。我希望这将导致新的社区驱动的构建管道生态系统。
- 项目内部更好的关注点分离。这将使添加新功能和平台变得更容易。
计划
这项工作已经进行了许多实验,看起来很不错。目前没有这项工作的具体时间表,但我希望到 2023 年第一季度结束时,将发布一个针对 Mac 的 alpha 版本,以便社区可以测试、实验并提供反馈。
摘要
- v2 API 是声明式的,向开发人员隐藏了很多东西,并且不适合多个窗口等特性。将创建一个新的 API,它将更简单、更直观、更强大。
- 构建系统不透明且难以自定义,因此我们将迁移到外部构建系统,这将使所有内容都开放。
- 绑定生成速度缓慢且复杂,因此我们将迁移到静态分析,这将消除当前方法中的许多复杂性。
v2 的核心已经投入了大量工作,并且很稳固。现在是时候解决它上面的层级了,让开发人员的体验变得更好。
我希望您与我一样对这一切感到兴奋。我期待着听到您的想法和反馈。
此致
‐Lea
PS: 如果您或您的公司发现 Wails 有用,请考虑赞助该项目。谢谢!
PPS:是的,这是使用 Wails 构建的多窗口应用程序的真实截图。它不是模型,是真实的。它很棒。它即将推出。