编译webassembly必须设goos=js且goarch=wasm;需配套wasm_exec.js胶水代码;go与js交互须用syscall/js.value;fmt.println默认不输出;异步操作需js回调;init()中避免阻塞。

编译前必须确认 GOOS 和 GOARCH 设置正确
Go 编译 WebAssembly 的核心是 GOOS=js 和 GOARCH=wasm,缺一不可。只设 GOARCH=wasm 而忽略 GOOS=js 会导致编译失败或生成无法运行的二进制——Go 1.12+ 后,GOOS=wasi 是另一条路径,但标准 Web 浏览器环境只认 js/wasm 组合。
常见错误现象:build constraints exclude all Go files,通常是因为源文件里写了 // +build js,wasm 但环境没设对,或者用了 main.go 却没导出 main 函数(WASM 模块不自动执行)。
- 终端中务必先执行:
GOOS=js GOARCH=wasm go build -o main.wasm main.go - 不要用
go run—— 它不支持 wasm 目标 - 确保
main.go中有func main() { ... },且不能依赖os.Args、log.Fatal等阻塞/系统级 API
浏览器中加载 wasm 需要配套的 syscall/js 运行时
Go 编译出的 .wasm 文件本身不能直接被 WebAssembly.instantiateStreaming 加载运行,它依赖 Go 自带的 JavaScript 胶水代码(runtime_js.js),这个文件位于 $GOROOT/misc/wasm/wasm_exec.js。
如果你跳过这一步,浏览器控制台会报错:ReferenceError: global is not defined 或 Go is not defined —— 因为缺少胶水层对 global、process 等 Node.js 概念的模拟,以及对 Go 运行时(如 goroutine 调度、GC)的封装。
- 复制胶水脚本:
cp $(go env GOROOT)/misc/wasm/wasm_exec.js . - HTML 中必须按顺序引入:
<script src="wasm_exec.js"></script>,再创建Go实例并调用run - 入口 Go 函数需显式暴露:用
syscall/js.FuncOf注册回调,并在 JS 侧调用go.run(result.instance)
syscall/js 的值传递限制很实际
Go 和 JS 之间不能直接传 struct、slice 或 channel,所有交互必须通过 syscall/js.Value 封装。这意味着你不能把一个 []byte 直接当 JS 的 Uint8Array 用,也不能把 JS 的 Promise 当 Go 的 chan 收。
典型翻车点:试图在 Go 里 fmt.Println(js.Global().Get("fetch")) 后直接 await,结果卡死——因为 Go 是同步模型,没有原生 Promise await 支持。
- JS → Go:参数自动转为
syscall/js.Value,可用.String()、.Float()、.Bool()提取;对象需用.Get("prop")访问字段 - Go → JS:返回值会被自动包装,但复杂结构建议序列化为 JSON 字符串再传回(
json.Marshal+.String()) - 异步操作(如 fetch)必须用
js.Global().Get("fetch").Invoke(...).Call("then", ...)链式调用 JS 回调,不能用 Go 的go func()
调试 wasm 时别依赖 fmt.Println
默认情况下,fmt.Println 输出不会出现在浏览器控制台,而是被丢弃。除非你手动重定向 os.Stdout 到 js.Global().Get("console").Get("log"),否则 print 语句等于不存在。
更麻烦的是:一旦某处 panic,WASM 实例会静默终止,且不打印堆栈——除非你提前用 js.SetFinalizer 或包裹 recover() 并主动调用 console.error。
- 最简调试法:在关键位置写
js.Global().Get("console").Call("log", "step 1", x) - 启用 Go 的 wasm 调试符号(仅开发用):
go build -gcflags="all=-N -l" -o main.wasm main.go,配合 Chrome 的 WebAssembly Debugging 工具查看源码映射 - 避免在
init()里做重操作——WASM 初始化阶段无事件循环,阻塞即白屏
WebAssembly 不是“把 Go 代码扔进浏览器就能跑”,它本质是受限沙箱:没有文件系统、无本地网络权限(fetch 受 CORS 约束)、goroutine 被降级为协程调度。最易被忽略的,是 JS 与 Go 生命周期的耦合——比如忘记在 JS 侧调用 go.exit(),会导致内存无法释放;又比如在 Go 函数返回后继续访问已释放的 js.Value,引发 silent crash。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











