自主的20%るぅる

各々が自主的に好き勝手書くゆるふわ会社ブログ

Go言語で正式にWebAssemblyがサポートされたので軽くDOM操作してみる

Go言語の 1.11 がリリースされました! WebAssembly サポート!

こんにちは!
江嵜です。

先日 Go 言語の 1.11 がリリースされましたね! (リリース記事はこちら)

記事を見ると、1.11 では module という機能の追加もあったようですが…
WebAssembly が正式にサポートされていますね!
今回はこちらの方に注目!

これは試してみなければ!というところで、軽く DOM 操作あたりまで触ってみましたー

WebAssembly とは?

WebAssembly とは、ざっくりいうと「バイナリコードをブラウザで動かす技術」といったところですね。
JavaScript はブラウザ上で解析・実行されるため、処理に時間がかかりがちですが、
WebAssembly によって、より高速にコードが実行できるようになるのですね。
また、ファイルサイズも小さくなることが見込めます。

詳しくは、検索すれば結構出てくるのでそちらで。

Go言語で WebAssembly しよう!

Go言語で WebAssembly が使えるようになったとの事なので、早速使ってみましょう!

基本は公式で管理されている github の wiki に従っていきます。

まず、 main.go として以下のファイルを作成しまして…

package main

func main() {
    println("Hello, WebAssembly!")
}

いわゆるハローワールドですね。
これをコンパイルしていきます。

今回は Windows で実施したので、PowerShell で main.go を作成したディレクトリに移動して、

set GOARCH=wasm
set GOOS=js
go build -o test.wasm main.go

としてビルドします。(GOARCH と GOOS は環境変数として渡されればよいです。 Mac 等の方はよしなに…)
すると、 main.go と同じディレクトリに test.wasm が出来上がります。

WebAssembly のファイルの作成はできましたが、これだけでは WebAssembly を体験することはできません。

あくまで Web から読みだす必要があるので、 HTML ファイルと、WebAssembly の実行をサポートする JS ファイルが必要になります。
それぞれ、
HTML: https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.html
JS: https://github.com/golang/go/blob/master/misc/wasm/wasm_exec.js
に配置されているので、それぞれ内容を持ってきて、先程の test.wasm ファイルと同じところへ配置しましょう。

最後に、これらのファイルを配信するサーバーが必要ですね。
こちらも Wiki の方に Go言語でテスト用の簡単なサーバーを構築するコードが掲載されていますので、
For a basic HTTP server のコードをコピーし、 go でコンパイルしてサーバーを作りましょう。

コンパイルして出来たサーバーの実行ファイルも、他のファイルと同じところへ配置し、
サーバー実行後 http://localhost:8080/wasm_exec.html へアクセスすると、晴れてページが表示されますね!

ページに表示されたボタンをクリックすることで、 WebAssembly が起動し、コンソールへテキストが表示されることが確認できます!

さらに… DOM 操作までやるよ!

これで「やったーできたー!」だけでも良かったのですが、
折角なのでもう少し踏み込んで DOM 操作もしてみましょう。

ついでに、先ほどのコードではボタンをクリックするまで WebAssembly が実行されていなかったので、
読み込みが完了した時点で自動的に実行までされるようにします。
まずは html ファイル

<!doctype html>
<!--
Copyright 2018 The Go Authors. All rights reserved.
-->
<html>

<head>
    <meta charset="utf-8">
    <title>Go wasm</title>
</head>

<body>
  <input id="textInput1" type="text" placeholder="textInput1">
  <input id="textInput2" type="text" placeholder="textInput2" disabled>
  <button id="button1" type="button">button1</button>
  <button id="button2" type="button">button2</button>
    <a href="http://wasm_exec.js">http://wasm_exec.js</a>

        if (!WebAssembly.instantiateStreaming) { // polyfill
            WebAssembly.instantiateStreaming = async (resp, importObject) => {
                const source = await (await resp).arrayBuffer();
                return await WebAssembly.instantiate(source, importObject);
            };
        }

        const go = new Go();
        let mod, inst;
        WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then(async (result) => {
            mod = result.module;
            inst = result.instance;

      console.log("wasm loaded.");
      await go.run(inst);
      inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
        });

</body>

</html>

input と button を 2 個。
あと読み出しの所を少し修正しましたね。

JS の方は変更なしです。
こちらは特に、手を入れるところはないですね。

で、 main.go ファイル

package main

import "syscall/js"

func main() {
    // Get で該当のオブジェクトを取得
    document := js.Global().Get("document")

    // メソッド名を Call の第一引数に渡して実行
    textInput1 := document.Call("getElementById", "textInput1")
    textInput2 := document.Call("getElementById", "textInput2")
    button1 := document.Call("getElementById", "button1")
    button2 := document.Call("getElementById", "button2")


    // 新規コールバック用関数の作成
    cb1 := js.NewCallback(func(args []js.Value) {
        // Set, Get でプロパティの参照・代入(Get は型指定を忘れないよう注意)
        textInput2.Set("value", textInput1.Get("value").String())
    })
    // 複数の引数も Call で渡せる
    button1.Call("addEventListener", "click", cb1)

    cb2 := js.NewCallback(func(args []js.Value) {
        // 関数は Invoke で実行
        js.Global().Get("alert").Invoke("button2 clicked!")
    })
    button2.Call("addEventListener", "click", cb2)


    // プログラムが終了しないよう、チャネルの待ち受けをする
    <-make(chan struct{}, 0)
}

結構行数が増えました。
基本的にはコメントを入れているので、そちらを参照していただくとして…

import "syscall/js" は JS を操作するためのライブラリですね。
ドキュメントは https://tip.golang.org/pkg/syscall/js/

js.Global でグローバルが取れてくるので、 .Get() を数珠つなぎにして目的のオブジェクトを取得、
Call() でメソッド実行ですね。

この辺りが使えれば、大抵のことは出来るかと思います。

あと、注意しなければならないのが、最後の <-make(chan struct{}, 0) ですね。

これがないと、最初に HTML から WebAssembly をロードした後、すぐ終了してしまい
イベントを発火させることができません。

イベントなどの非同期処理を行いたい場合は、プログラムの最後にチャネルの入力を配置し、
ここで待機させて終了しないようにしましょう。

これを先ほど同様実行してみると…

いい感じで動作していますね!

まとめ

Go言語は Version 1.11 から、 WebAssembly に対応になりました!
最近は Web の可能性がどんどん広がっていますね!

これからは 3D レンダリングが必要なゲームなどの重い処理でも、ガンガン Web で実現できるようになっていくかもしれません。

もちろん、今すぐこれを使え!というものではないですが(コード見てもらったら分かりますが、まだ ベタで DOM 操作を書く必要があったりして、規模の大きい開発はムリがありますね)今後の Web 界にまた一つ可能性が広がったところではないでしょうか。

皆さんも、ぜひトライしてみてくださいね。

Let’s share this article!

{ 関連記事 }

{ この記事を書いた人 }

takato_ezaki
記事一覧