# 应用开发

使用 Wails 开发应用程序没有硬性规定，但有一些基本准则。

## 应用程序设置

默认模板使用 `main.go` 配置和运行应用程序, 同时`app.go` 用于定义应用程序逻辑.

`app.go`文件将定义一个结构体，该结构体有 2 个方法作为主应用程序的回调：

```go title="app.go"
type App struct {
    ctx context.Context
}

func NewApp() *App {
    return &App{}
}

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

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

- 一旦 Wails 分配了它需要的资源，就会调用 startup 方法，它是创建资源、设置事件侦听器以及应用程序在启动时需要的任何其他东西的好地方。 它提供了一个 `context.Context`， 通常保存在一个结构体字段中。 调用 [运行时](../reference/runtime/intro) 需要此`context.Context`。 如果此方法返回错误，则应用程序将终止。 在开发模式下，错误会输出到控制台。

- Shutdown 方法将在关闭过程结束时由 Wails 调用。 这是释放内存和执行关闭任务的好地方。

`main.go` 文件通常由对`wails.Run()`的单个调用组成，它接受应用程序配置。 模板使用的模式是，在调用 `wails.Run()` 之前， 我们创建并保存一个在 `app.go` 中定义的结构体的实例在名为 `app` 的变量中。 这个配置是我们添加回调的地方：

```go {3,9,10} title="main.go"
func main() {

    app := NewApp()

    err := wails.Run(&options.App{
        Title:             "My App",
        Width:             800,
        Height:            600,
        OnStartup:  app.startup,
        OnShutdown: app.shutdown,
    })
    if err != nil {
        log.Fatal(err)
    }
}

```

可以在 [此处](../howdoesitwork#应用程序生命周期回调) 找到有关应用程序生命周期回调的更多信息。

## 绑定方法

您可能希望从前端调用 Go 方法。 这通常是通过向 `app.go` 中已经定义的结构体中添加公共方法来实现的：

```go {16-18} title="app.go"
type App struct {
    ctx context.Context
}

func NewApp() *App {
    return &App{}
}

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

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

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

在主应用程序中，`Bind` 字段是我们告诉 Wails 想要绑定什么：

```go {11-13} title="main.go"
func main() {

    app := NewApp()

    err := wails.Run(&options.App{
        Title:             "My App",
        Width:             800,
        Height:            600,
        OnStartup:  app.startup,
        OnShutdown: app.shutdown,
        Bind: []interface{}{
            app,
        },
    })
    if err != nil {
        log.Fatal(err)
    }
}

```

这将绑定 `App` 结构中的所有公共方法(它永远不会绑定 startup 和 shutdown 方法)。

### 绑定多个结构体时处理 context

如果您想为多个结构绑定方法，但希望每个结构都保留对 context 的引用，以便您可以使用运行时函数，一个好的方式是将上下文从 `OnStartup` 方法传递给您的结构实例：

```go
func main() {

    app := NewApp()
    otherStruct := NewOtherStruct()

    err := wails.Run(&options.App{
        Title:             "My App",
        Width:             800,
        Height:            600,
        OnStartup:  func(ctx context.Context){
            app.SetContext(ctx)
            otherStruct.SetContext(ctx)
        },
        OnShutdown: app.shutdown,
        Bind: []interface{}{
            app,
            otherStruct
        },
    })
    if err != nil {
        log.Fatal(err)
    }
}
```

Also you might want to use Enums in your structs and have models for them on frontend. In that case you should create array that will contain all possible enum values, instrument enum type and bind it to the app:

```go {16-18} title="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"},
}
```

In the main application configuration, the `EnumBind` key is where we can tell Wails what we want to bind enums as well:

```go {11-13} title="main.go"
func main() {

    app := NewApp()

    err := wails.Run(&options.App{
        Title:             "My App",
        Width:             800,
        Height:            600,
        OnStartup:  app.startup,
        OnShutdown: app.shutdown,
    Bind: []interface{}{
        app,
    },
    EnumBind: []interface{}{
        AllWeekdays,
     },
    })
    if err != nil {
        log.Fatal(err)
    }
}

```

This will add missing enums to your `model.ts` file.

可以在 [此处](../howdoesitwork.mdx#method-binding) 找到有关绑定的更多信息。

## 应用程序菜单

Wails 支持向您的应用程序添加菜单。 这是通过将 [菜单结构体](../reference/menus#菜单结构体) 传递给应用程序配置来完成的。 常见做法是使用一个返回菜单的方法，更常见的是用作生命周期回调的 `App` 结构体上的方法。

```go {11} title="main.go"
func main() {

    app := NewApp()

    err := wails.Run(&options.App{
        Title:             "My App",
        Width:             800,
        Height:            600,
        OnStartup:  app.startup,
        OnShutdown: app.shutdown,
        Menu:       app.menu(),
        Bind: []interface{}{
            app,
        },
    })
    if err != nil {
        log.Fatal(err)
    }
}

```

## 资产

Wails v2 处理资源的方式的伟大之处在于它不需要处理！ 您唯一需要给 Wails 的是一个 `embed.FS`。 你如何做到这一点完全取决于你。 您可以像 vanilla 模板一样使用 vanilla html/css/js 文件。 您可能有一些复杂的构建系统，但这并不影响。

运行 `wails build` 时，它会检查项目根目录的 `wails.json` 文件。 文件中有 2 个字段会被读取：

- "frontend:install"
- "frontend:build"

第一个，如果有给定，将在 `frontend` 目录中执行以安装 node 模块。 第二个，如果有给定，将在 `frontend` 目录中执行以构建前端项目。

如果没有给出这两个字段，那么 Wails 不会对前端做任何操作。 它仅仅被用作 `embed.FS`。

### 资产处理程序

Wails v2 应用程序可以选择在 `options.App` 中定义一个 `http.Handler`，它允许连接到 AssetServer 以动态创建文件或处理 POST/PUT 请求。 GET 请求总是首先由 `assets` FS 处理。 如果 FS 没有找到请求的文件，请求将被转发到 `http.Handler` 服务。 如果指定，除了 GET 以外的任何请求都将由 `AssetsHandler` 直接处理。 也可以仅通过将 `nil` 指定为 `Assets` 选项来使用 `AssetsHandler`。

## 内置开发服务器

运行 `wails dev` 将启动内置的开发服务器，它将在您的项目目录中启动一个文件监听器。 默认情况下，如果有任何文件更改，wails 会检查它是否是应用程序文件（默认：.go，可使用 `-e` 标志配置）。 如果是，那么它将重新构建您的应用程序并重新启动它。 如果更改的文件在 assetdir 目录中，它会在很短的时间后重新加载。

开发服务器使用一种称为“防抖”的技术，这意味着它不会立即重新加载，因为可能会在短时间内更改多个文件。 当触发发生时，它会在发出重新加载之前等待一定的时间。 如果发生另一个触发，它会再次重置为等待时间。 默认情况下，此值为 100ms。 如果此值不适用于您的项目，则可以使用 `-debounce` 标志进行配置。 如果使用，此值将保存到您的项目配置中并成为默认值。

## 外部开发服务器

一些框架带有自己的实时重新加载服务器，但是它们将无法利用 Wails Go 绑定。 在这种情况下，最好运行一个监听脚本，将项目重新构建到构建目录中，Wails 将监视该目录。 有关示例，请参阅使用 [rollup](https://rollupjs.org/guide/en/) 的默认 svelte 模板。

### Create React App

The process for a Create-React-App project is slightly more complicated. In order to support live frontend reloading the following configuration needs to be added to your `wails.json`:

```json
  "frontend:dev:watcher": "yarn start",
  "frontend:dev:serverUrl": "http://localhost:3000",
```

The `frontend:dev:watcher` command will start the Create-React-App development server (hosted on port `3000` typically). The `frontend:dev:serverUrl` command then instructs Wails to serve assets from the development server when loading the frontend rather than from the build folder. In addition to the above, the `index.html` needs to be updated with the following:

```html
    <head>
        <meta name="wails-options" content="noautoinject" />
        <script src="/wails/ipc.js"></script>
        <script src="/wails/runtime.js"></script>
    </head>
```

This is required as the watcher command that rebuilds the frontend prevents Wails from injecting the required scripts. This circumvents that issue by ensuring the scripts are always injected. With this configuration, `wails dev` can be run which will appropriately build the frontend and backend with hot-reloading enabled. Additionally, when accessing the application from a browser the React developer tools can now be used on a non-minified version of the application for straightforward debugging. Finally, for faster builds, `wails dev -s` can be run to skip the default building of the frontend by Wails as this is an unnecessary step.

## Go 模块

默认的 Wails 模板会生成一个包含模块名称“changeme”的 `go.mod` 文件。 您应该在项目生成后将其更改为更合适的内容。
