簡単なWebAssemblyを実装
モジュールディレクトリの作成とgo.modを生成します。
mkdir wasm
cd wasm
go mod init example.com/wasm
main.goを作成します。
package main
import "fmt"
func main() {
fmt.Println("Hello, WebAssembly!")
}
Go言語のビルドコマンドに環境変数GOOS = js、GOARCH = wasmを設定して、main.goをWebAssembly用にコンパイルしてmain.wasmを生成します。
GOOS = js GOARCH = wasm go build -o main.wasm
main.wasmだけでは動かないので、wasm_exec.js(JavaScriptサポートファイル)をコピーします。
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
wasm_exec.jsを使ってmain.wasmを実行するindex.htmlを作成します。
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body></body>
</html>
あとは、WebサーバーのDocumentRootにindex.html、main.wasm、wasm_exec.jsを配置してブラウザでアクセスすれば、コンソール(開発ツール)に「Hello, WebAssembly!」の文字列が表示されます。
DocumentRoot
├── index.html
├── main.wasm
└── wasm_exec.js
Go言語でJavaScriptを制御する
このままだとmain.wasmが実行後すぐに終了してしまい、JavaScriptのイベントを受け取れません。そこでチャンネルを生成して、チャンネルを受信するまでmain関数をブロックするようにします。
func main(){
done := make(chan int)
// 処理
<-done
}
JavaScriptを制御するためのsyscall/jsモジュールをimportします。
import "syscall/js"
syscall/jsのパッケージを使えば、JavaScriptをGo言語から制御することも、JavaScriptからGo言語を実行することも可能です。
JavaScriptからGo言語を実行するためのJavaScriptの関数をGo言語で定義する
var goFunc js.Func
defer goFunc.Release()
goFunc = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
arg1 := args[0]
log.Printf("arg1=%v", arg1) // ブラウザのコンソールに出力
return nil
})
js.Global().Set("goFunc", goFunc)
Go言語からJavaScriptのjsFunc関数を実行する
js.Global().Get("jsFunc").Invoke(arg1)
※ wasmを実行しているHtmlにjsFunc関数が定義されている必要があります。
JavaScriptのEventListenerにGo言語で定義したJavaScriptの関数を登録する
var onChangeMyText js.Func
defer onChangeMyText.Release()
onChangeMyText = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
event := args[0] // eventオブジェクト
myText = event.Get("target").Get("value").String() // event.terget.valueのJSコードと同等
return nil
})
js.Global().
Get("document").
Call("getElementById", "myText").
Call("addEventListener", "change", onChangeMyText)
beforeunloadmイベントにmainを止めているチャンネルにメッセージを送るメソッドを登録する
var onBeforeunload js.Func
defer onBeforeunload.Release()
onBeforeunload = js.FuncOf(func(this js.Value, args []js.Value) interface{} {
done <- 1 // チャンネルに値を送信してアプリを終了する
return nil
})
js.Global().
Call("addEventListener", "beforeunload", onBeforeunload)