Wails v3初识

4,246 阅读5分钟

介绍

简介与官网链接

Wails 是一个用于构建桌面应用程序的框架,允许开发者使用 Go 语言编写后端逻辑,并结合前端技术(如 HTML、CSS 和 JavaScript)来创建跨平台的桌面应用。它的目标是简化桌面应用的开发流程,同时利用 Go 的高效性和现代前端技术的灵活性。

v3 官网链接

v2 官网链接

特点

Wails 是一个可让您使用 Go 和 Web 技术编写桌面应用的项目。

将它看作为 Go 的快并且轻量的 Electron 替代品。 您可以使用 Go 的灵活性和强大功能,结合丰富的现代前端,轻松的构建应用程序。

快速启动模板

Wails 带有许多预配置的模板,可让您快速启动和运行应用程序。 有以下框架的模板:Svelte、React、Vue、Preact、Lit 和 Vanilla。 每个模板都有 JavaScript 和 TypeScript 版本。

原生元素

Wails 使用专门构建的库来处理窗口、菜单、对话框等原生元素,因此您可以构建美观、功能丰富的桌面应用程序。

It does not embed a browser, so it delivers a small runtime. Instead, it reuses the native rendering engine for the platform. 在 Windows 上,是基于 Chromium 构建的新 Microsoft Webview2 库。

Go 和 JavaScript 互操作

Wails 自动使您的 Go 方法可用于 JavaScript,因此您可以从前端按名称调用它们! 它甚至会生成 Go 方法使用的结构体的 TypeScript 版本,因此您可以在 Go 和 JavaScript 之间传递相同的数据结构。

运行时库

Wails 为 Go 和 JavaScript 提供了一个运行时库,它可以处理现代应用程序需要的很多东西,比如事件、日志记录、对话框等。

实时开发体验

自动重新构建

当您在“开发”模式下运行您的应用程序时,Wails 会将您的应用程序构建为原生桌面应用程序,但会从磁盘读取您的资源。 它将检测您的 Go 代码的任何更改并自动重新构建和重新启动您的应用程序。

自动重新加载

当检测到对您的应用程序资产的更改时,您正在运行的应用程序将“重新加载”,几乎立即反映您的更改

在浏览器中开发您的应用程序

如果您更喜欢在浏览器中调试和开发,那么 Wails 可以满足您的需求。 正在运行的应用程序还有一个网络服务器,它将在连接到它的任何浏览器中运行您的应用程序。 当您的资源在磁盘上发生变化时,它会刷新。

可用于生产的原生二进制文件

当您准备好完成应用程序的最终构建时,CLI 会将其编译为单个可执行文件,并将所有资源打包到其中。 在 Windows 和 MacOS 上,可以创建用于分发的原生包。 使用打包工具后生成的资源(图标、info.plist、清单文件等)是您项目的一部分,可以自定义,让您完全控制应用程序的构建方式。

工具

Wails CLI 提供了一种简单的方法来生成、构建和打包您的应用程序。 它将完成创建图标的繁重工作,使用最佳设置编译您的应用程序,并提供可分发的、可用于生产的二进制文件。 可以从许多入门模板中进行选择,以快速启动和运行!

视频教程

基于Golang的Wails开发Redis桌面工具

使用Wails3创建第一个桌面应用程序

展示视频

使用Vue和Go开发桌面软件:基于wails2实现的桌面应用开发模板

Quick Start

本节将展示 wails v3 创建一个 go + react-ts 项目,并实现一些基本的js 与 go互操作

配置环境

linux 环境会安装在 GOPATH 路径下的bin目录

go install -v github.com/wailsapp/wails/v3/cmd/wails3@latest

运行以下命令,检测wails桌面开发的一些其他依赖是否安装完成

一般需要一个webview环境(windows自带),gcc环境,npm(用web技术当然需要这个)

wails3 doctor

使用模板创建项目

# wails init -n <项目名> -t <模板>
wails3 init -n wailstest -t react-ts

创建完成后用IDE打开文件夹,看到目录结构如下图所示

运行初始项目

注意要配置npm代理以及go代理,否则超级慢

wails3 dev

运行结果如图

JS调用Go方法

创建一个Go结构体

package main

import "os"

type JSFunc struct{}

// GetCurrentDir 获取当前运行目录
func (j *JSFunc) GetCurrentDir() (string, error) {
	return os.Getwd()
}

绑定Go方法

在main方法中,application.OptionsServices 数组添加新的Service并传入刚刚创建的结构体

保存代码之后,会发现前端代码中自动生成了js方法

调用Go方法

在App.jsx页面中,导入 GetCurrentDir 并使用以异步方式调用Go方法

import { useEffect, useState } from "react"
import { GetCurrentDir } from '../bindings/changeme/jsfunc'


function App() {
  const [workDir, setWorkDir] = useState('')

  useEffect(() => { // 组件挂载自动调用
    GetCurrentDir()
      .then(data => {
        setWorkDir(data)
      })
      .catch(err => {
        alert(err)
      })

  }, [])

  return (
    <div>
      <p>当前工作目录{workDir}</p>    
    </div>
  )
}

export default App

最终效果

Go调用JS方法

在js中可以监听来自go发出的事件,来达到Go调用Js方法的效果

import { useEffect, useState } from "react"
import { Events } from "@wailsio/runtime"


function App() {
  const [count, setCount] = useState(0)

  useEffect(() => { // 组件挂载自动调用
    let cancel = Events.On('onTip', () =>  { // 注册事件
      setCount(count+1)

    })

    return () => {
      cancel() // count 变化后,取消注册,重新注册一个新的函数(详见react.useEffect)
    }
  }, [count])



  return (
    <div>
      <p>count: {count}</p>    
    </div>
  )
}

export default App
func main() {
    //....

	// Create a new Wails application by providing the necessary options.
	// Variables 'Name' and 'Description' are for application metadata.
	// 'Assets' configures the asset server with the 'FS' variable pointing to the frontend files.
	// 'Bind' is a list of Go struct instances. The frontend has access to the methods of these instances.
	// 'Mac' options tailor the application when running an macOS.
	app := application.New(application.Options{
		Name:        "wailstest",
		Description: "A demo of using raw HTML & CSS",
		Services: []application.Service{
			application.NewService(&GreetService{}),
			application.NewService(&JSFunc{}),
		},
		Assets: application.AssetOptions{
			Handler: application.AssetFileServerFS(assets),
		},
		Mac: application.MacOptions{
			ApplicationShouldTerminateAfterLastWindowClosed: true,
		},
	})

	go func() {
		for {
			time.Sleep(1 * time.Second) // 每秒钟Emit事件

			app.EmitEvent("onTip")
		}

	}()

    //....
}

效果