ボタンを押すとGoで実装された関数が呼ばれその中でテキストの値が更新される。

$ go version
go version go1.18 darwin/amd64
syscall/js packageでjs側の変数を参照したり関数を呼び出すことができる。 Go 1.12で破壊的変更があり NewCallback() は FuncOf() に変更された。 返り値は js.ValueOf() に対応している型である必要がありstructを返すとエラーになる。
package main
import (
"strconv"
"syscall/js"
)
func increment(this js.Value, args []js.Value) any {
counter := js.Global().Get("document").Call("getElementById", "counter")
counterValue, err := strconv.ParseInt(counter.Get("textContent").String(), 10, 64)
if err != nil {
return map[string]any{"error": err.Error()}
}
counterValue += int64(args[0].Int())
counter.Set("textContent", counterValue)
return map[string]any{"message": counterValue}
}
func main() {
js.Global().Set("goIncrement", js.FuncOf(increment))
select {} // keep running
}
GOOS=js, GOARCH=wasm でビルドすると .wasmファイルが生成される。 Goの.wasmファイルはサイズが大きくなる傾向があり、このファイルは1.3MBとなっている。 TinyGoでビルドすると165KBと大幅に小さくなる。
$ GOOS=js GOARCH=wasm go build -o main.wasm main.go
$ tinygo build -o wasm.wasm -target wasm ./main.go
公式のリポジトリにある wasm_exec.js を読み込み WebAssembly.instantiateStreaming で.wasmファイルをコンパイルしロードする。
<html>
<head>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
function increment(value) {
ret = goIncrement(value)
console.log(ret)
}
</script>
</head>
<body>
<div id="counter">0</div>
<button onClick="increment(1)">+1</button>
</body>
これらを配信するサーバーを立ち上げて動作確認する。
$ npx http-server
$ open localhost:8080/index.html
参考
Go WebAssembly Tutorial - Building a Calculator Tutorial | TutorialEdge.net
javascript - Golang’s syscall/js js.NewCallback is undefined - Stack Overflow
How do WebAssembly binaries compiled from different languages compare in size? - Stack Overflow