Go Wiki: WebAssembly
はじめに
Go 1.11では、WebAssemblyへの実験的なポートが追加されました。Go 1.12では、いくつかの部分が改善され、Go 1.13ではさらなる改善が期待されています。Go 1.21では、WASI syscall APIをターゲットとする新しいポートが追加されました。
WebAssemblyについては、そのホームページで次のように説明されています。
WebAssembly(略称Wasm)は、スタックベースの仮想マシンのためのバイナリ命令フォーマットです。Wasmは、C/C++/Rustのような高水準言語のコンパイルのためのポータブルなターゲットとして設計されており、クライアントおよびサーバーアプリケーションのWebへのデプロイを可能にします。
WebAssemblyを初めて使用する場合は、はじめにセクションを読み、Go WebAssemblyの講演をいくつか視聴し、以下のその他の例をご覧ください。
JavaScript (GOOS=js) ポート
はじめに
このページでは、Go 1.11以降が正常にインストールされていることを前提としています。トラブルシューティングについては、インストールのトラブルシューティングページを参照してください。
Windowsを使用している場合は、Git BashなどのBASHエミュレーションシステムを使用して、このチュートリアルに従うことをお勧めします。
Go 1.23以前では、この記事で必要なwasmサポートファイルは`misc/wasm`にあり、`lib/wasm/wasm_exec.js`のようなファイルで操作を実行する際には、パスを置き換える必要があります。
基本的なGoパッケージをWeb用にコンパイルするには
package main
import "fmt"
func main() {
fmt.Println("Hello, WebAssembly!")
}
WebAssembly用にコンパイルするには、`GOOS=js`と`GOARCH=wasm`環境変数を設定します
$ GOOS=js GOARCH=wasm go build -o main.wasm
これにより、パッケージがビルドされ、main.wasmという名前の実行可能なWebAssemblyモジュールファイルが生成されます。.wasmファイル拡張子により、後で正しいContent-Typeヘッダーを使用してHTTP経由で提供することが容易になります。
mainパッケージのみコンパイルできることに注意してください。そうでない場合、WebAssemblyで実行できないオブジェクトファイルが生成されます。WebAssemblyで使用したいパッケージがある場合は、それをmainパッケージに変換してバイナリをビルドします。
main.wasmをブラウザで実行するには、JavaScriptサポートファイルと、すべてを接続するためのHTMLページも必要です。
JavaScriptサポートファイルをコピーします
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>
ブラウザがまだ`WebAssembly.instantiateStreaming`をサポートしていない場合は、ポリフィルを使用できます。
次に、3つのファイル(`index.html`、`wasm_exec.js`、`main.wasm`)をWebサーバーから提供します。たとえば、`goexec`を使用します
# install goexec: go install github.com/shurcooL/goexec@latest
goexec 'http.ListenAndServe(`:8080`, http.FileServer(http.Dir(`.`)))'
または、独自の基本的なHTTPサーバーコマンドを使用します。
注:コンパイラと`wasm_exec.js`サポートファイルは、同じメジャーGoバージョンを一緒に使用する必要があります。つまり、`main.wasm`ファイルがGoバージョン1.Nを使用してコンパイルされている場合、対応する`wasm_exec.js`ファイルもGoバージョン1.Nからコピーする必要があります。その他の組み合わせはサポートされていません。
注:Unixライクシステムで`goexec`コマンドを動作させるには、Goのパス環境変数をシェルの`profile`に追加する必要があります。Goの入門ガイドでは、これを次のように説明しています。
/usr/local/go/binをPATH環境変数に追加します。これは、/etc/profile(システム全体へのインストールの場合)または$HOME/.profileに次の行を追加することで実行できます
export PATH=$PATH:/usr/local/go/bin
注:プロファイルファイルに加えられた変更は、次回コンピュータにログインするまで適用されない場合があります
最後に、http://localhost:8080/index.htmlに移動し、JavaScriptデバッグコンソールを開くと、出力が表示されます。プログラムを変更し、`main.wasm`を再構築し、更新して新しい出力を確認できます。
Node.jsを使用したWebAssemblyの実行
コンパイルされたWebAssemblyモジュールをブラウザではなくNode.jsを使用して実行することができ、これはテストと自動化に役立ちます。
まず、Nodeがインストールされ、`PATH`に含まれていることを確認してください。
次に、`$(go env GOROOT)/lib/wasm`を`PATH`に追加します。これにより、`go run`と`go test`は`PATH`検索で`go_js_wasm_exec`を見つけて、`js/wasm`で動作させることができます
$ export PATH="$PATH:$(go env GOROOT)/lib/wasm"
$ GOOS=js GOARCH=wasm go run .
Hello, WebAssembly!
$ GOOS=js GOARCH=wasm go test
PASS
ok example.org/my/pkg 0.800s
Go自体で作業している場合は、`run.bash`をシームレスに実行することもできます。
`go_js_wasm_exec`は、Go WasmバイナリをNodeで実行できるようにするラッパーです。デフォルトでは、Goインストールの`lib/wasm`ディレクトリにあります。
`PATH`に何も追加したくない場合は、`go run`または`go test`を手動で実行するときに、`-exec`フラグを`go_js_wasm_exec`の場所に設定することもできます。
$ GOOS=js GOARCH=wasm go run -exec="$(go env GOROOT)/lib/wasm/go_js_wasm_exec" .
Hello, WebAssembly!
$ GOOS=js GOARCH=wasm go test -exec="$(go env GOROOT)/lib/wasm/go_js_wasm_exec"
PASS
ok example.org/my/pkg 0.800s
最後に、ラッパーを使用してGo Wasmバイナリを直接実行することもできます
$ GOOS=js GOARCH=wasm go build -o mybin .
$ $(go env GOROOT)/lib/wasm/go_js_wasm_exec ./mybin
Hello, WebAssembly!
$ GOOS=js GOARCH=wasm go test -c
$ $(go env GOROOT)/lib/wasm/go_js_wasm_exec ./pkg.test
PASS
ok example.org/my/pkg 0.800s
ブラウザでのテストの実行
wasmbrowsertestを使用して、ブラウザ内でテストを実行することもできます。これは、Webサーバーの起動を自動化し、ヘッドレスChromeを使用してその内部でテストを実行し、ログをコンソールにリレーします。
前と同じように、`go get github.com/agnivade/wasmbrowsertest`を実行してバイナリを取得します。それを`go_js_wasm_exec`に名前変更し、`PATH`に配置します
$ mv $GOPATH/bin/wasmbrowsertest $GOPATH/bin/go_js_wasm_exec
$ export PATH="$PATH:$GOPATH/bin"
$ GOOS=js GOARCH=wasm go test
PASS
ok example.org/my/pkg 0.800s
または、`exec`テストフラグを使用します。
GOOS=js GOARCH=wasm go test -exec="$GOPATH/bin/wasmbrowsertest"
DOMとのインタラクション
https://pkg.go.dev/syscall/jsを参照してください。
また
-
`app`: PWA互換の、Reactベースのフレームワーク(カスタムツーリング付き)。
-
`dom`: DOM操作を合理化するためのライブラリが開発中です。
-
`dom`: JavaScript DOM APIのGoバインディング。
-
`domui`: 完全なGUIアプリケーションを作成するための純粋なGoフレームワーク。
-
`gas`: WebAssemblyアプリケーション用のコンポーネントベースのフレームワーク。
-
GoWebian: 純粋なGoでページを構築し、WebAssemblyバインディングを追加するためのライブラリ。
-
`hogosuru`: ブラウザのほとんどの機能(indexeddb、serviceworker、websocketなど)をGOで直接アクセスできるように実装した高度なwebassemblyフレームワーク。
-
VECTY: WebAssemblyを使用してGoでレスポンシブで動的なWebフロントエンドを構築し、ReactやVueJSなどの最新のWebフレームワークと競合します。
-
`vert`: GoとJSの値間のWebAssembly相互運用。
-
`vue`: WebAssemblyアプリケーションのためのプログレッシブフレームワーク。
-
Vugu: アプリケーションロジックにGoを使用するHTMLレイアウト、単一ファイルコンポーネント、迅速な開発とプロトタイピングワークフローを備えたwasm Web UIライブラリ。
-
`webapi`: DOM、HTML、WebGLなどのバインディングジェネレーターと生成されたバインディング。
-
`webgen`: HTMLでコンポーネントを定義し、`webapi`を使用してGoタイプとコンストラクタ関数を生成します。
キャンバス
- 新しいキャンバス描画ライブラリ-非常に効率的です。
net/httpの使用中にフェッチオプションを設定する
net/httpライブラリを使用してGoからHTTPリクエストを作成すると、それらはfetch呼び出しに変換されます。ただし、fetch オプションとhttp クライアントオプションの間には直接的なマッピングはありません。これを達成するために、fetchオプションとして認識される特別なヘッダー値がいくつかあります。それらは -
-
`js.fetch:mode`:Fetch APIモード設定のオプション。有効な値は:「cors」、「no-cors」、「same-origin」、「navigate」です。デフォルトは「same-origin」です。
-
`js.fetch:credentials`:Fetch API資格情報設定のオプション。有効な値は:「omit」、「same-origin」、「include」です。デフォルトは「same-origin」です。
-
`js.fetch:redirect`:Fetch APIリダイレクト設定のオプション。有効な値は:「follow」、「error」、「manual」です。デフォルトは「follow」です。
そのため、たとえば、リクエストの作成中にモードを「cors」に設定する場合、次のようなものになります
req, err := http.NewRequest("GET", "http://localhost:8080", nil)
req.Header.Add("js.fetch:mode", "cors")
if err != nil {
fmt.Println(err)
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer resp.Body.Close()
// handle the response
詳細なコンテキストと場合によっては新しい情報については、#26769にご登録ください。
ChromeのWebAssembly
Chromeの新しいバージョンを実行している場合、Liftoff(新しいコンパイラ)を有効にするフラグ(`chrome://flags/#enable-webassembly-baseline`)があり、読み込み時間を大幅に短縮するはずです。詳細はこちらこちら。
その他の例
一般
- Shimmer - Goを使用したwasmでの画像変換。ライブデモ。
- ビデオフィルタリング - ウェブカメラからのビデオのフィルター(ソースコード)
- HandyTools - base64エンコーディング/デコーディング、Unix時間の変換などのツールを提供します(ライブデモ)
キャンバス(2D)
- GoWasm Experiments - いくつかの一般的な呼び出しタイプの動作コードを示しています
- Gomeboycolor-wasm
- 実験的なGameboy ColorエミュレータのWASMポート。対応するブログ記事には、興味深い技術的洞察が含まれています。
- TinyGoキャンバス
- これは、標準のgoではなくTinyGoでコンパイルされているため、19.37kB(圧縮)のwasmファイルになります。
- 車とマウス
- カーソルで小さなキャンバスに描かれた車を誘導してポイントを獲得するゲームです。
データベース
- TiDB-Wasm - Go言語データベースであるTiDBをブラウザ上でWasmで実行します。
WebGL キャンバス (3D)
- 基本的な三角形 (ソースコード) - WebGLで基本的な三角形を作成します。
- TinyGoに移植したもの (ソースコード) - 圧縮後約14kB(メインラインGoバージョンのサイズの3%)
- 回転する立方体 (ソースコード) - WebGLで回転する立方体を作成します。
- TinyGoに移植したもの (ソースコード) - 圧縮後約23kB(メインラインGoバージョンのサイズの4%)
- Splashy (ソースコード) - 画面をクリックしてペイントを生成します…
WASI (GOOS=wasip1) ポート
はじめに (WASI)
Go 1.21では、WASIがサポートされるプラットフォームとして導入されました。WASI用にビルドするには、wasip1
ポートを使用します。
$ GOOS=wasip1 GOARCH=wasm go build -o main.wasm
公式ブログには、WASIポートの使用に関する役立つ紹介があります:https://go.dokyumento.jp/blog/wasi。
Go WebAssemblyに関する講演
エディタの設定
- GoLandとIntellij UltimateのWebAssembly設定 - GoLandとIntellij UltimateでWasmを動作させるための具体的な手順を示します。
デバッグ
WebAssemblyは*まだ*デバッガをサポートしていないため、JavaScriptコンソールに出力するには、当面は昔ながらのprintln()
アプローチを使用する必要があります。
公式のWebAssemblyデバッグサブグループが設立され、初期調査と提案が行われています。
デバッガに興味がある方は、ぜひ参加して推進にご協力ください。 :smile
WebAssemblyファイルの構造の分析
WebAssemblyコードエクスプローラは、WebAssemblyファイルの構造を視覚化するのに役立ちます。
- 左側の16進数の値をクリックすると、それが属するセクションと、右側の対応するテキスト表現が強調表示されます。
- 右側の行をクリックすると、左側の16進数のバイト表現が強調表示されます。
Wasmファイルサイズの削減
現在、Goは大きなWasmファイルを生成し、最小サイズは約2MBです。Goコードがライブラリをインポートする場合、このファイルサイズは劇的に増加する可能性があります。10MB以上は一般的です。
このファイルサイズを削減するには、主に2つの方法があります(今のところ)。
-
.wasm ファイルを手動で圧縮します。
gz
圧縮を使用すると、約2MB(最小ファイルサイズ)のWASMファイルの例が約500kBに削減されます。gzip --best
よりも良い結果が得られるため、Zopfliを使用してgzip圧縮を行う方が良い場合がありますが、実行にははるかに時間がかかります。- 圧縮にBrotliを使用すると、ファイルサイズはZopfliと
gzip --best
の両方よりも著しく優れており、圧縮時間もその中間程度です。この(新しい)Brotliコンプレッサは妥当に見えます。
@johanbrandhorstからの例
例1
サイズ コマンド 圧縮時間 16M (非圧縮サイズ) 該当なし 2.4M brotli -o test.wasm.br test.wasm
53.6秒 3.3M go-zopfli test.wasm
3分2.6秒 3.4M gzip --best test.wasm
2.5秒 3.4M gzip test.wasm
0.8秒 例2
サイズ コマンド 圧縮時間 2.3M (非圧縮サイズ) 該当なし 496K brotli -o main.wasm.br main.wasm
5.7秒 640K go-zopfli main.wasm
16.2秒 660K gzip --best main.wasm
0.2秒 668K gzip main.wasm
0.2秒 https://github.com/lpar/gzippedのようなものを使用して、利用可能な場合は、正しいヘッダーを持つ圧縮ファイルを自動的に提供します。
-
Wasmファイルの生成にTinyGoを使用します。
TinyGoは、組み込みデバイスを対象としたGo言語のサブセットをサポートし、WebAssembly出力ターゲットを備えています。
制限はありますが(まだ完全なGo実装ではありません)、かなり高機能であり、生成されるWasmファイルは…小さいです。約10kBは珍しくありません。「Hello world」の例は575バイトです。
gz -6
すると、408バイトに減少します。 :winkこのプロジェクトも非常に活発に開発されているため、その機能は急速に拡張されています。TinyGoでWebAssemblyを使用する方法の詳細については、https://tinygo.org/docs/guides/webassembly/を参照してください。
その他のWebAssemblyリソース
- Awesome-Wasm - Wasmリソースの広範なリスト。Go固有ではありません。
このコンテンツはGo Wikiの一部です。