简介
文档:https://wails.io/zh-Hans/docs/introduction/
github:https://github.com/wailsapp/wails
将它看作为 Go 的快并且轻量的 Electron 替代品。 您可以使用 Go 的灵活性和强大功能,结合丰富的现代前端,轻松的构建应用程序。
之前使用Electron开发过一个简单的桌面应用,打包后居然有一百多M,它不仅嵌入 Chromium 和 Node.js 到 二进制文件,而且包含完整的前端代码,对比,但是 Electron有一个超级应用,那就是 vscode。
功能
- 原生菜单、对话框、主题和半透明
- Windows、macOS 和 linux 支持
- 内置 Svelte、React 、Preact 、Vue、Lit 和 Vanilla JS 的模板
- 从 JavaScript 轻松调用 Go 方法
- 自动将 Go 结构体转换为 TypeScript 模块
- Windows 上不需要 CGO 或外部 DLL
- 使用 Vite 的实时开发模式
- 可以轻松创建、构建和打包应用的强大命令行工具
- 丰富的 运行时库
- 使用 Wails 构建的应用程序兼容 Apple & Microsoft 商店
检查环境
要求:go 1.18+,node 15+
node --version
v20.11.0
npm --version
10.2.5
go version
go version go1.20 windows/amd64
安装 wails-cli
go install github.com/wailsapp/wails/v2/cmd/wails@latest
运行
wails doctor
将检查您是否安装了正确的依赖项。 如果没有,它会就缺少的内容提供建议以帮助纠正问题。输出如下
Wails Doctor
# Wails
Version | v2.9.2
# System
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| OS | Windows 10 Education || Version |1803(Build: 17134)|| ID ||| Go Version | go1.20 || Platform | windows || Architecture | amd64 || CPU | Intel(R) Core(TM) i7-4790 CPU @ 3.60GHz || GPU 1| OrayIddDriver Device (Shanghai Best Oray Information Technology Co., Ltd.) - Driver: 17.1.58.818 || GPU 2| Intel(R) HD Graphics 4600(Intel Corporation) - Driver: 20.19.15.4835 || Memory | 16GB |
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
# Dependencies
┌───────────────────────────────────────────────────────┐
| Dependency | Package Name | Status | Version || WebView2 | N/A | Installed |130.0.2849.80 || Nodejs | N/A | Installed |20.11.0 ||npm| N/A | Installed |10.2.5 || *upx | N/A | Installed | upx 4.1.0 || *nsis | N/A | Installed | v3.09 |
└─────────────── * - Optional Dependency ───────────────┘
# Diagnosis
SUCCESS Your system is ready for Wails development!
♥ If Wails is useful to you or your company, please consider sponsoring the project:
https://github.com/sponsors/leaanthony
创建项目
wails init -n wailsapp-1 -t vue
以dev模式运行
cd wailsapp-1
wails dev
会启动一个窗口展示页面,你还可以在浏览器上访问 http://localhost:34115/ 来打开你的页面,以方便在页面上调试。还有一个是 http://localhost:5173/,是 Vite Server,暂时不用管。
从任务管理器看到,使用的是 Microsoft Edge Webview2,但是Webview2底层依赖的是 Chromium ,查看介绍。
Ctrl+Shift+F12,或者右键–>检查,可以打开
DevTools
,其目标是
http://wails.localhost:34115
。当然,在 build 的时候可以设置禁用 DevTools。这个跟你在浏览器按F12打开的基本一样,功能差不多。
在启动的过程中命令行报了一个错误
Generating bindings: 2024/11/17 15:20:34 Error: open D:\dev\php\magook\trunk\server\wailsapp-1\frontend\wailsjs\runtime\package.json: Access is denied.
后来我使用管理员来运行 wails 程序还是有这个问题,暂时搁置。
端口5173是 npm 的 server,虽然也能打开网站,但是无法调用go的方法,因为没有执行bindings操作,因此缺少必要的js文件。
文档中也提到:当 Wails 为您的
index.html
提供服务时,默认情况下,它会将 2 个脚本注入
<body>
标签以加载
/wails/ipc.js
和
/wails/runtime.js
。 这些文件分别安装绑定和运行时。然后这两个js文件是不在你的项目里的,或者说还有很多文件是wails生成的然后被注入到 embed.FS 中,这个在下面讲原理的时候再说。
修改前端代码会立即在窗口中看到,修改了go代码也会自动重启程序。
在启动的时候会将go程序暴雷的方法生成js对应的方法,所以你只需要调用即可。
src就是一个纯粹的前端项目,比如你可以使用element-plus框架,前端通过调用wailsjs与go进行通信。
编译打包
wails build
,打包后保存在 build 目录下。编译后只有8.69M,压缩后会更小,还是比较轻量级的,但是它的运行需要 WebView2 ,如果你电脑上没有,APP在运行的时候会先下载,但是谁的电脑上还不装个浏览器啥的,目前的windows系统都会自带 Edge,所以问题不大。
其思路大致为:将前端页面打包为
embed.FS
,以二进制的形式包含在go代码中,这个在golang项目中是常规操作。然后就是使用go调用webkit窗口,可以设置菜单,事件等功能。
它继承了go的跨平台编译特性,所以wails也是可以打包指定平台的应用,当然也有一些细节需要注意,文档中有说明。
它支持压缩二进制文件,使你的软件更小,比如upx,当然go本身也支持压缩编译。
在编译的时候还可以设置 Webview2 参数,用来处理客户机器上的依赖问题,参考。
所以,wails 这种实现比操作系统层面的UI组件要好用很多,虽然它们也会做跨平台的底层兼容(比如Fyne),但是使用HTML来堆UI交互显然容易很多;其次它比Electron要轻很多。
架构实现
我比较感兴趣的是它的
JS Bingdings for Go Methods
,即 js 是如何调用go方法以及如何拿到返回值。
可以先了解一下JSBridge的实现原理
- 一文让你彻底理解JSBridge
- 微信小程序渲染层与逻辑层独立 及JsBridge原理分析
- https://mp.weixin.qq.com/s/sw71vDr_gXsJ-peI2WAmTQ
我将使用 DevTools 来进行说明。
打开源代码,就能看到很多不在你项目中的文件
从 bindings.js 可以看到wails以map的形式,将go方法在js中做了绑定,将方法注册到了window上,window代表当前窗口,
window.go
和
window['go']
是一个意思。我们在DevTools控制台输入
console.log(window);
,就能看到你注册的方法。
window.go[packageName][structName][methodName]=function(){...}
所以调用方式为
exportfunctionGreet(arg1){return window['go']['main']['App']['Greet'](arg1);}
从 ipc.js 我们可以看到它在检查环境并定义
window.WailsInvoke
(()=>{(function(){letn=function(e){for(var s = window[e.shift()]; s && e.length;)
s = s[e.shift()];return s
}, o =n(["chrome","webview","postMessage"]), t =n(["webkit","messageHandlers","external","postMessage"]);if(!o &&!t){
console.error("Unsupported Platform");return}
o &&(window.WailsInvoke=e=> window.chrome.webview.postMessage(e)),
t &&(window.WailsInvoke=e=> window.webkit.messageHandlers.external.postMessage(e))})();})();
shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
从
call.js
中的
call()
方法可以看到发送的消息结构
const payload ={
name,
args,
callbackID,};// Make the call
window.WailsInvoke('C'+JSON.stringify(payload));
关于
window.postMessage()
,它的作用是
向浏览器中的指定窗口发送消息
,定义如下
window.postMessage(message, targetOrigin, [transfer])
- message:发送到其他window的信息,字符串。
- targetOrigin:目标窗口的url,空或者*表示向所有窗口广播消息。
- transfer(可选参数):是一串和 message 同时传递的 Transferable 对象。这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
接收方需要通过监听事件的方式来获得消息
window.addEventListener("message",(e)=>{},false)
消息 e 包含三个属性:
- data:从其他 window 中传递过来的数据字符串。
- orgin:发送数据的源地址url。
- source:发送数据的window对象,您可以使用此来在具有不同 origin 的两个窗口之间建立双向通信。
调试
calls.js
文件的 Callback 函数,以查看go返回的数据。打断点,然后在窗口上输入8888,点击Great按钮。
incomingMessage
是一个json字符串
{"result":"Hello 8888, It's show time ok.","error":null,"callbackid":"main.App.Greet-3143955801"}
callbackid
是自动设置的,我们绑定的go方法可以有两个返回值
(result interface{}, err error)
,当然
err
是可选的。
从 Callback 函数的最后几行代码我们就知道在js代码中如何处理返回值,修改 HelloWorld.vue
functiongreet(){Greet(data.name).then(result=>{
data.resultText = result
}).catch(err=>{
data.resultText = err
})}
修改go中的Great方法
// Greet returns a greeting for the given namefunc(a *App)Greet(name string)(string,error){return fmt.Sprintf("Hello %s, It's show time ok.", name), errors.New("test error")}
等待它重新启动。
看到返回值为
{"result":null,"error":"test error","callbackid":"main.App.Greet-3845362984"}
至此,整个流程就很清晰了。
具体的细节就不深究了,大致思路就是:利用 WebView2 为平台,js向窗口发送数据,go程序调用WebView2来监听消息事件,这样就拿到了数据,go向js响应数据也采用同样的方式,它们使用 callbackID 来一一对应。
之前在使用QQ音乐网页版的时候,注意到一个场景,你将查到的歌曲播放,它会新开一个播放页,也就是现在有两个页面,一个主页一个播放页,你在主页切歌了,播放页立马响应。
版权归原作者 raoxiaoya 所有, 如有侵权,请联系我们删除。