[Go] WebAssembly について調べる
はじめに
ハッカソンで使う予定ができたので、調べます。
前提
Go 1.11 以降のバージョンに限ります。
wasm ビルドしてみる
main.go
を作成し、適当にログを出すコードを書きます。
package main
import "fmt"
func main() {
fmt.Println("Hello, WebAssembly!")
}
これをビルドします。
wasm として .go
ファイルをビルドするには、コンパイル時に GOOS
および GOARCH
変数を設定します。
GOOS=js GOARCH=wasm go build -o main.wasm
実行すると、-o
オプションで指定した名前のファイル(今回は main.wasm
)が生成されます。
実行してみる
実際に Web サイト上で実行してみます。
wasm を実行するには、Go の lib/wasm
内にある wasm_exec.js
が必要です。これをプロジェクトファイルに持ってきます。
cp "$(go env GOROOT)/lib/wasm/wasm_exec.js" .
存在しない場合は、公式リポジトリから直接持ってきてください。
実行する土台となる 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>
index.html
をお好きなブラウザーで開くと、開発者ツールのコンソールにログが吐き出されているのがわかります。
ちらと見ましたが、主要なブラウザの最新バージョンでは問題なく対応していそう。
DOM にアクセスする
できることを調べていきます。
どうやら syscall/js
ライブラリを介して DOM からアクセスするっぽい。
ドキュメントを取得する
index.html
のドキュメント情報を Go 内で扱ってみます。
先に取得するタイトルと h1
タグを追記して、
<html>
<head>
+ <title>WASM Sample</title>
<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>
+ <h1>Hello WASM!</h1>
</body>
</html>
それぞれ取得してみます。
//go:build js && wasm
package main
import (
"fmt"
"syscall/js"
)
func main() {
window := js.Global()
document := window.Get("document")
fmt.Println("title: " + document.Get("title").String())
fmt.Println("h1: " + document.Call(("querySelector"), "h1").Get("innerText").String())
}
実行。問題なさそう。(wasm の再ビルドを忘れずに!)
js/dom
を使用して取得する
公式ドキュメントを見てたら、より便利そうな DOM 操作ライブラリがありました。
早速使ってみます。
go get honnef.co/go/js/dom/v2
//go:build js && wasm
package main
import (
"fmt"
- "syscall/js"
+ "honnef.co/go/js/dom/v2"
)
func main() {
+ window := dom.GetWindow()
+ document := window.Document()
+ fmt.Println("title: " + document.GetElementsByTagName("title")[0].TextContent())
+ fmt.Println("h1: " + document.QuerySelector("h1").TextContent())
- window := js.Global()
- document := window.Get("document")
- fmt.Println("title: " + document.Get("title").String())
- fmt.Println("h1: " + document.Call(("querySelector"), "h1").Get("innerText").String())
}
例がアレなのでわかりづらいですが、QuerySelector
などに対応しています。先程の例では document.Call
からすべての関数を呼ぶ必要があったので、こちらのほうが保守性が高そうでいい感じ。
参考
Discussion