使用Golang和WebAssembly进行客户端Web开发

使用Golang和WebAssembly进行客户端Web开发 大家好,

我注意到关于 Go 1.11 中新增的 WebAssembly 支持的讨论还不多,因此决定亲自尝试并探索其功能。在使用过程中收获了不少乐趣,现在想分享一些代码来鼓励其他 Go 程序员开始使用 WebAssembly。

Go 在服务端网页编程领域广受欢迎,而现在它也能在客户端发挥作用。

以下是我创建的一个小型 Go WebAssembly 应用:http://jayts.com/vp/

这个程序完全使用 Go 编写,没有写一行 JavaScript!(虽然需要 JavaScript 胶水代码来加载应用,但这部分直接复制自 $GOHOME/misc/wasm。)

代码仓库在这里:https://github.com/Yaoir/VideoPoker-Go-WebAssembly

README 文件包含了游戏操作说明。HTML、CSS 和 Go 代码均附有注释,方便理解 WebAssembly 的工作原理。如果你想尝试创建自己的 WebAssembly 应用,这份代码可以作为起点。我还准备了一个更简单的 Go WebAssembly 示例,可能更适合入门学习:https://github.com/Yaoir/ClockExample-Go-WebAssembly

希望使用 Mac 和 iOS 设备的用户能通过 Safari、Firefox、Chrome 和 Opera 浏览器测试视频扑克应用在苹果产品上的运行效果。目前唯一发现的问题是在安卓 4.2 平板的 Firefox 浏览器上存在兼容性。总体而言,WebAssembly 的兼容性表现良好。


更多关于使用Golang和WebAssembly进行客户端Web开发的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

jayts: 我想听听Mac的看法

Safari仍在加载中。但Chrome可以正常工作… 😊

更多关于使用Golang和WebAssembly进行客户端Web开发的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


嗨 Jay,

自从看到 Go 1.11 更新后,我就想尝试一下 Web Assembly。感谢你提供的代码!

将 Opera 添加到支持列表中,刚刚在 macOS 10.13.6 上进行了测试。如果对大家有帮助的话,在 Linux Mint 19 的 Opera 浏览器上也能正常运行。

Google Pixel 2 Android设备上,无论是内置浏览器还是Chrome浏览器对我来说都无法正常使用。一直卡在"正在加载,请稍候"的界面。

R

以下部分将解决关于管理和客户端网页开发的认知问题,这些问题与WebAssembly相关,因此在Chrome中可以正常工作,但有时会卡在加载状态。

在使用三星Plus(arm64)上的Chrome OS Crostini时: 时钟示例运行正常,但视频扑克游戏在"游戏正在加载"处卡住了。

// 代码示例保留原样

在 iOS 的 Safari 浏览器上运行正常!

imagine

在 Chrome、Firefox 和 Safari 浏览器中,该功能在 macOS 10.14.1(最新版本)上运行正常。我可以在周日测试 iOS 版本。做得不错!唯一注意到的区别是在 Chrome 版本中我获胜的次数更多了。

也许你的浏览器缓存在这里跟你开了个玩笑?你可以通过添加未使用的查询参数来引用wasm文件,比如使用修改时间作为参数。例如 myprogram.wasm?t=78888776,这样新版本就会始终被加载,而不会被浏览器使用缓存版本。如果你无法自动修改js文件或HTML文件,是否可以考虑在URL中显式更改版本号?例如使用 myprogram.wasm?version=1.0.1,并在每次更新wasm时修改这个版本号。

以下是最新进展。

自从我发布这个主题以来,我了解到Go对WebAssembly的支持存在以下问题:

GitHub问题:cmd/compile: wasm代码导致Android版Chrome和Firefox出现内存不足错误

该问题的描述与我观察到的现象高度吻合,因此我认为这就是问题的根源。

最近我从Gopher Slack工作区的Christine Dodrill那里了解到,可以使用TinyGo生成非常小的wasm文件(千字节级别而非兆字节级别),而且这些wasm应用可以在移动设备上运行而不会遇到"内存不足"错误。不过,TinyGo功能非常有限且不够成熟。我尝试用它编译视频扑克应用,但未能成功编译。总之,对于想要探索其他方案的人来说,这值得关注。

感谢大家的帮助测试!很高兴知道在Mac上运行正常。希望它在iOS上也能同样顺利运行。

我在HTML文件中尝试了不同的JavaScript粘合代码,但在Android的Firefox上仍然遇到同样的问题。我想知道这个问题是否特定于我使用的旧版Android,还是也会影响更新的Android版本。

希望能获得更多关于这方面的测试帮助,这样我就能确定是否需要将其作为Go/wasm或Firefox的bug进行上报。(要帮忙的话,请在Android上启动Firefox并运行该应用,然后重新加载页面。这个bug的表现是应用永远无法完成加载。清除浏览器缓存可以修复这个问题。有时,快速多次点击重新加载按钮也能解决。)

如果有专家看到这里,问题的关键在于JavaScript粘合代码在WebAssembly.instantiateStreaming()调用处卡住了。

// 代码示例保留原文

以下是针对您分享的Go WebAssembly项目的专业评论,重点分析技术实现并提供示例代码:

技术实现分析

  1. WebAssembly编译流程
# 设置目标平台为wasm
GOOS=js GOARCH=wasm go build -o main.wasm main.go

需要配套使用wasm_exec.js加载器,该文件位于$GOROOT/misc/wasm/目录。

  1. DOM操作示例
package main

import (
    "syscall/js"
)

func main() {
    doc := js.Global().Get("document")
    body := doc.Call("getElementById", "app")
    
    // 创建按钮元素
    btn := doc.Call("createElement", "button")
    btn.Set("innerHTML", "Go WASM Button")
    btn.Call("addEventListener", "click", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        js.Global().Get("console").Call("log", "Button clicked from Go!")
        return nil
    }))
    
    body.Call("appendChild", btn)
    
    // 保持Go程序运行
    select {}
}
  1. 性能优化建议
  • 使用js.Func包装Go函数时注意内存管理
  • 通过js.GC()主动触发垃圾回收
  • 避免频繁的Go-WASM边界数据交换

兼容性处理

针对您提到的Android 4.2兼容性问题,可添加特征检测:

// 检测WebAssembly支持
if js.Global().Get("WebAssembly").IsUndefined() {
    js.Global().Get("console").Call("warn", "WebAssembly not supported")
    return
}

构建配置示例

// go:build js && wasm
// +build js,wasm

package main

import "syscall/js"

func registerCallbacks() {
    js.Global().Set("goFunc", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        // 处理逻辑
        return nil
    }))
}

您的项目展示了Go在客户端开发的可行性,特别是完整的游戏实现证明了WASM的成熟度。视频扑克的DOM操作和事件处理实现为同类项目提供了重要参考。

回到顶部