go 1.11+原生支持webassembly,但需配合wasm_exec.js胶水脚本、正确mime类型(application/wasm)及js.wait()阻塞主goroutine才能在浏览器运行。
go 1.11 起原生支持 webassembly,但默认生成的 wasm 文件不能直接在浏览器运行——它缺少 javascript 胶水代码和内存初始化逻辑,直接 fetch 并 webassembly.instantiate 会报错 runtimeerror: unreachable 或 missing imports。
用 GOOS=js GOARCH=wasm go build 生成 wasm 文件
这是最基础的编译命令,但要注意三点:
- 必须使用 Go 1.11+,且标准库中部分包(如
net/http、os/exec)在 wasm 环境下不可用或行为受限 - 输出文件是纯 wasm 字节码,不带任何运行时支撑,无法独立加载
- 生成的
main.wasm默认依赖syscall/js,因此main函数必须调用js.Wait()阻塞,否则程序立即退出
示例 main.go:
package main
import "syscall/js"
func main() {
js.Global().Set("add", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
return args[0].Int() + args[1].Int()
}))
js.Wait()
}
必须搭配 $(go env GOROOT)/misc/wasm/wasm_exec.js
浏览器不理解 Go 的 wasm 运行时约定,需要官方提供的胶水脚本做桥接。这个文件不是可选的,漏掉就无法启动。
- 路径固定,每次 Go 升级后建议重新复制,旧版
wasm_exec.js可能与新版.wasm不兼容 - 需通过
<script src="wasm_exec.js"></script>在 HTML 中引入,且必须在实例化 wasm 前加载完成 - 该脚本会自动检测
WebAssembly.instantiateStreaming支持情况,并 fallback 到手动 fetch + compile
浏览器中加载需用 WebAssembly.instantiateStreaming + 正确 MIME 类型
直接 fetch("main.wasm").then(r => r.arrayBuffer()).then(WebAssembly.compile) 容易失败,因为 Go 生成的 wasm 含自定义 section,部分浏览器要求 streaming 初始化。
- 本地开发时,HTTP 服务器必须返回
application/wasmMIME 类型,否则 Chrome/Firefox 拒绝执行 - 用 Python 快速起服务:
python3 -m http.server --bind localhost:8080不行(无 wasm MIME),改用python3 -m http.server 8080并配合mime.types补丁,或直接用serve(npm)、caddy等支持 wasm 的工具 - HTML 中典型加载模式:
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
调试时常见错误:no `env` or `go` imports / `js.valueGet` panic
这类错误基本都指向胶水脚本未正确加载,或 wasm 文件被浏览器拦截(MIME 错误、CORS、缓存旧版 wasm_exec.js)。
- 检查浏览器控制台 Network 面板,确认
main.wasm响应头含Content-Type: application/wasm - 确保
wasm_exec.js和main.wasm来自同一域名和协议,避免跨域问题 -
js.Value方法(如Call、Get)只能在 Go 主 goroutine 或由 JS 触发的回调中安全调用;在新 goroutine 中直接操作会 panic
真正麻烦的从来不是编译这一步,而是胶水脚本版本对齐、MIME 配置、以及 JS/Go 交互边界上那些隐式约束——它们不会报错在编译期,只在 runtime 突然崩掉。











