
简介
Wails 是一个项目,它简化了使用 Go 编写跨平台桌面应用程序的能力。它使用原生 webview 组件作为前端(而不是嵌入式浏览器),将世界上最流行的 UI 系统的强大功能带入 Go,同时保持轻量级。
版本 2 于 2022 年 9 月 22 日发布,并带来了许多增强功能,包括
- 实时开发,利用流行的 Vite 项目
- 用于管理窗口和创建菜单的丰富功能
- 微软的 WebView2 组件
- 生成反映 Go 结构的 Typescript 模型
- 创建 NSIS 安装程序
- 混淆构建
现在,Wails v2 提供了强大的工具来创建丰富、跨平台的桌面应用程序。
这篇博文旨在探讨该项目目前的状况以及我们可以改进的地方。
我们现在在哪里?
自 v2 发布以来,看到 Wails 的受欢迎程度不断上升,真是令人难以置信。我不断地被社区的创造力和他们用 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 中调用它们。目前执行此操作的方法有点像 hack。它涉及使用一个特殊的标志构建应用程序,然后运行生成的二进制文件,该二进制文件使用反射来确定已绑定了哪些内容。这会导致一个循环依赖问题:在没有绑定文件的情况下无法构建应用程序,在没有构建应用程序的情况下无法生成绑定文件。解决这个问题的方法有很多,但最好的方法是不使用这种方法。
之前曾尝试编写 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 构建的多窗口应用程序的真实屏幕截图。它不是模型。它是真实的。它很棒。它即将推出。