跳至主要内容
版本:v2.9.0

它是如何工作的?

Wails 应用程序是一个标准的 Go 应用程序,具有一个 webkit 前端。应用程序的 Go 部分包含应用程序代码和运行时库,该库提供许多有用的操作,例如控制应用程序窗口。前端是一个 webkit 窗口,它将显示前端资产。前端还可以使用 JavaScript 版本的运行时库。最后,可以将 Go 方法绑定到前端,这些方法将显示为可以调用的 JavaScript 方法,就像它们是本地 JavaScript 方法一样。

主应用程序

概述

主应用程序包含对 wails.Run() 的单个调用。它接受应用程序配置,该配置描述了应用程序窗口的大小、窗口标题、要使用的资产等。一个基本的应用程序可能如下所示

main.go
package main

import (
"embed"
"log"

"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)

//go:embed all:frontend/dist
var assets embed.FS

func main() {

app := &App{}

err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
OnStartup: app.startup,
OnShutdown: app.shutdown,
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}


type App struct {
ctx context.Context
}

func (b *App) startup(ctx context.Context) {
b.ctx = ctx
}

func (b *App) shutdown(ctx context.Context) {}

func (b *App) Greet(name string) string {
return fmt.Sprintf("Hello %s!", name)
}

选项概述

此示例设置了以下选项

  • Title - 应出现在窗口标题栏中的文本
  • Width & Height - 窗口的尺寸
  • Assets - 应用程序的前端资产
  • OnStartup - 窗口创建并即将开始加载前端资产时的回调
  • OnShutdown - 应用程序即将退出时的回调
  • Bind - 我们希望公开给前端的结构实例切片

完整的应用程序选项列表可以在 选项参考 中找到。

资产

Assets 选项是必需的,因为没有前端资产,你就无法拥有 Wails 应用程序。这些资产可以是您期望在 Web 应用程序中找到的任何文件——html、js、css、svg、png 等。不需要生成资产捆绑包——普通文件就可以了。应用程序启动时,它将尝试从您的资产加载 index.html,从那时起,前端将基本上充当浏览器。值得注意的是,对 embed.FS 中文件的位置没有任何要求。嵌入路径很可能使用相对于主应用程序代码的嵌套目录,例如 frontend/dist

main.go
//go:embed all:frontend/dist
var assets embed.FS

在启动时,Wails 将迭代嵌入文件以查找包含 index.html 的目录。所有其他资产将相对于此目录加载。

由于生产二进制文件使用 embed.FS 中包含的文件,因此应用程序不需要附带任何外部文件。

使用 wails dev 命令在开发模式下运行时,资产将从磁盘加载,任何更改都会导致“实时重新加载”。资产的位置将从 embed.FS 推断。

更多详细信息可以在 应用程序开发指南 中找到。

应用程序生命周期回调

在前端即将加载 index.html 之前,将回调到 OnStartup 中提供的函数。此方法将传递一个标准的 Go 上下文。在调用运行时时需要此上下文,因此一个标准模式是在此方法中保存对它的引用。在应用程序关闭之前,OnShutdown 回调将以相同的方式调用,同样使用上下文。还有一个 OnDomReady 回调,用于前端完成在 index.html 中加载所有资产时,并且等效于 JavaScript 中的 body onload 事件。也可以通过设置选项 OnBeforeClose 来挂接到窗口关闭(或应用程序退出)事件。

方法绑定

Bind 选项是 Wails 应用程序中最重要的选项之一。它指定了哪些结构方法要公开给前端。将结构视为传统 Web 应用程序中的“控制器”。应用程序启动时,它会检查选项中 Bind 字段中列出的结构实例,确定哪些方法是公开的(以大写字母开头),并将生成这些方法的 JavaScript 版本,以便前端代码可以调用它们。

注意

Wails 要求您传递一个结构的实例,以便它能够正确地绑定它

在此示例中,我们创建一个新的 App 实例,然后将此实例添加到 wails.Run 中的 Bind 选项中

main.go
package main

import (
"embed"
"log"

"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
)

//go:embed all:frontend/dist
var assets embed.FS

func main() {

app := &App{}

err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{
app,
},
})
if err != nil {
log.Fatal(err)
}
}


type App struct {
ctx context.Context
}

func (a *App) Greet(name string) string {
return fmt.Sprintf("Hello %s!", name)
}

您可以绑定任意数量的结构。只需确保创建一个实例并将其传递到 Bind

    //...
err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{
app,
&mystruct1{},
&mystruct2{},
},
})

您也可以绑定枚举类型。在这种情况下,您应该创建一个数组,该数组将包含所有可能的枚举值,对枚举类型进行检测并将其通过 EnumBind 绑定到应用程序

app.go
type Weekday string

const (
Sunday Weekday = "Sunday"
Monday Weekday = "Monday"
Tuesday Weekday = "Tuesday"
Wednesday Weekday = "Wednesday"
Thursday Weekday = "Thursday"
Friday Weekday = "Friday"
Saturday Weekday = "Saturday"
)

var AllWeekdays = []struct {
Value Weekday
TSName string
}{
{Sunday, "SUNDAY"},
{Monday, "MONDAY"},
{Tuesday, "TUESDAY"},
{Wednesday, "WEDNESDAY"},
{Thursday, "THURSDAY"},
{Friday, "FRIDAY"},
{Saturday, "SATURDAY"},
}
    //...
err := wails.Run(&options.App{
Title: "Basic Demo",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
Assets: assets,
},
Bind: []interface{}{
app,
&mystruct1{},
&mystruct2{},
},
EnumBind: []interface{}{
AllWeekdays,
},
})

当您运行 wails dev(或 wails generate module)时,将生成一个前端模块,其中包含以下内容

  • 所有绑定方法的 JavaScript 绑定
  • 所有绑定方法的 TypeScript 声明
  • 所有用作绑定方法的输入或输出的 Go 结构的 TypeScript 定义

这使得从前端调用 Go 代码变得非常简单,可以使用相同的强类型数据结构。

前端

概述

前端是 webkit 渲染的一组文件。它就像一个浏览器和 web 服务器合二为一。您几乎1没有限制可以使用哪些框架或库。前端与 Go 代码之间交互的主要点是

  • 调用绑定 Go 方法
  • 调用运行时方法

调用绑定 Go 方法

使用 wails dev 运行应用程序时,它将自动在名为 wailsjs/go 的目录中为您的结构生成 JavaScript 绑定(您也可以通过运行 wails generate module 来执行此操作)。生成的 文件反映了应用程序中的包名称。在上面的示例中,我们绑定了 app,它有一个公开方法 Greet。这将导致生成以下文件

wailsjs
└─go
└─main
├─App.d.ts
└─App.js

这里我们可以看到,有一个 main 包,它包含绑定 App 结构的 JavaScript 绑定,以及这些方法的 TypeScript 声明文件。要从我们的前端调用 Greet,我们只需导入该方法并像调用普通 JavaScript 函数一样调用它

// ...
import { Greet } from "../wailsjs/go/main/App";

function doGreeting(name) {
Greet(name).then((result) => {
// Do something with result
});
}

TypeScript 声明文件为您提供了绑定方法的正确类型

export function Greet(arg1: string): Promise<string>;

生成的方法返回一个 Promise。成功调用会导致 Go 调用中的第一个返回值传递给 resolve 处理程序。不成功调用是指 Go 方法将错误类型作为其第二个返回值返回时,将错误实例传递回调用方。这将通过 reject 处理程序传递回来。在上面的示例中,Greet 仅返回一个 string,因此 JavaScript 调用永远不会拒绝——除非传递给它的数据无效。

所有数据类型都在 Go 和 JavaScript 之间正确转换。即使是结构也是如此。如果您从 Go 调用中返回一个结构,它将作为 JavaScript 类返回到您的前端。

注意

结构字段必须具有有效的 json 标签才能包含在生成的 TypeScript 中。

目前不支持匿名嵌套结构。

可以将结构发送回 Go。任何作为参数传递的 JavaScript 映射/类,如果期望一个结构,将被转换为该结构类型。为了使这个过程更容易,在 dev 模式下,会生成一个 TypeScript 模块,定义绑定方法中使用的所有结构类型。使用此模块,可以构建和将本机 JavaScript 对象发送到 Go 代码。

还支持使用结构体作为其签名的 Go 方法。绑定方法指定的所有 Go 结构(无论是作为参数还是作为返回类型)都将作为 Go 代码包装模块的一部分自动生成 TypeScript 版本。使用这些,可以在 Go 和 JavaScript 之间共享相同的数据模型。

示例:我们将 Greet 方法更新为接受 Person 而不是字符串

main.go
type Person struct {
Name string `json:"name"`
Age uint8 `json:"age"`
Address *Address `json:"address"`
}

type Address struct {
Street string `json:"street"`
Postcode string `json:"postcode"`
}

func (a *App) Greet(p Person) string {
return fmt.Sprintf("Hello %s (Age: %d)!", p.Name, p.Age)
}

wailsjs/go/main/App.js 文件将仍然包含以下代码

App.js
export function Greet(arg1) {
return window["go"]["main"]["App"]["Greet"](arg1);
}

wailsjs/go/main/App.d.ts 文件将使用以下代码更新

App.d.ts
import { main } from "../models";

export function Greet(arg1: main.Person): Promise<string>;

如我们所见,"main" 命名空间是从新的 "models.ts" 文件导入的。此文件包含绑定方法使用的所有结构定义。在此示例中,这是一个 Person 结构。如果我们查看 models.ts,我们可以看到模型是如何定义的

models.ts
export namespace main {
export class Address {
street: string;
postcode: string;

static createFrom(source: any = {}) {
return new Address(source);
}

constructor(source: any = {}) {
if ("string" === typeof source) source = JSON.parse(source);
this.street = source["street"];
this.postcode = source["postcode"];
}
}
export class Person {
name: string;
age: number;
address?: Address;

static createFrom(source: any = {}) {
return new Person(source);
}

constructor(source: any = {}) {
if ("string" === typeof source) source = JSON.parse(source);
this.name = source["name"];
this.age = source["age"];
this.address = this.convertValues(source["address"], Address);
}

convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map((elem) => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}

只要您的前端构建配置中包含 TypeScript,就可以以下列方式使用这些模型

mycode.js
import { Greet } from "../wailsjs/go/main/App";
import { main } from "../wailsjs/go/models";

function generate() {
let person = new main.Person();
person.name = "Peter";
person.age = 27;
Greet(person).then((result) => {
console.log(result);
});
}

生成的绑定和 TypeScript 模型的组合创造了一个强大的开发环境。

有关绑定的更多信息,请参阅 绑定方法 部分,该部分位于 应用程序开发指南 中。

调用运行时方法

JavaScript 运行时位于 window.runtime 处,包含许多方法来执行各种任务,例如发出事件或执行日志记录操作

mycode.js
window.runtime.EventsEmit("my-event", 1);

有关 JS 运行时的更多详细信息,请参阅 运行时参考


  1. 有一小部分库使用 WebView 中不支持的功能。对于此类情况,通常存在替代方案和解决方法。