📑

GolangでWebAssembly入門してみた

2023/08/05に公開

はじめに

WebAssemblyそういえば全く触ったことなかったな〜ということで、入門してみることにしました。

WebAssemblyとは

WebAssemblyとは、スタックベースの仮想マシン用のバイナリ命令フォーマットです。
元々はWebブラウザ上で、JavaScriptでは速度が出せない処理を高速で実行するためのバイナリフォーマットとして開発されました。
現在では、Webブラウザ上だけではなく、それ以外の環境のユースケースでも採用されるケースもあるようです。

※WebAssemblyのユースケースの例はこちらを参照

WebAssembly入門

さらっとどんなものなのかを押さえたところで、早速簡単なサンプルを使って、WebAssemblyを動かしてみたいと思います。

作るもの

MarkdownのEditorを作ります。
WebAssembly側でMarkdownのテキストをHTMLに変換する処理を用意し、それをブラウザから呼び出して使う形で作ります。

環境

  • Golang 1.20.2

MarkdownをHTMLに変換する処理を作る

GolangでまずMarkdownをHTMLに変換する処理をWebAssemblyとして作ります。

まず、エントリーポイントとなるmain.goを書きます。

package main

import (
	"syscall/js"
	"to-markdown/markdown"
)

func MarkdownToHtml(_ js.Value, args []js.Value) any {

	// Markdown文字列を入力値として受け取り、
	// HTMLに変換して返す
	in := args[0].String()
	out := markdown.ToHtml(in)
	return out
}

func main() {
	c := make(chan struct{}, 0)

	// js.Global().Set()を実行することで、
	// JavaScript側からGoの関数MarkdownToHtmlを
	// markdownToHtmlという名前で呼び出せるように登録している
	js.Global().Set("markdownToHtml", js.FuncOf(MarkdownToHtml))

	<-c
}

次にHTMLに変換する処理を書きます。

package markdown

import (
	"bytes"
	"fmt"
	"github.com/yuin/goldmark"
	"strings"
)

func ToHtml(in string) string {
	inBuf := []byte(in)
	var outBuf bytes.Buffer
	if err := goldmark.Convert(inBuf, &outBuf); err != nil {

		fmt.Printf("fail to convert markdown: %v\n", err)

		// 暫定仕様として、変換失敗時には元の文字列をそのまま返す仕様にする
		return in
	}

	// 変換後の文字列には改行コードが含まれているので削除する
	out := strings.ReplaceAll(outBuf.String(), "\n", "")
	return out
}

これで必要な処理は実装できたので、実行用のバイナリを作成します。
次のコマンドで、Goのコードをビルドします。

GOOS=js GOARCH=wasm go build -o markdown-to-html.wasm

作ったMarkdown変換処理をJSから呼び出す

作ったwasmの処理を使用し、ブラウザでMarkdownのテキストをHTMLに変換する処理を作ります。

まず、wasm形式のファイルをブラウザに読み込むためのjsファイルをコピーします。jsファイルはローカルのGo環境を保存している場所にあり、次のコマンドでコピー可能です。

cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

次にWebのエントリーポイントとなるindex.htmlを作ります。

<html>

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

<body>
<h1>Go WebAssembly</h1>

<!-- wasmファイルを扱うために必要なjsファイルを読み込む -->
<script src="./wasm_exec.js"></script>

<script>
    // markdown-to-html.wasmファイルをブラウザに読み込む
    const go = new Go();
    WebAssembly.instantiateStreaming(fetch("markdown-to-html.wasm"), go.importObject).then((result) => {
        const module = result.module;
        const instance = result.instance;
        go.run(instance);
        document.getElementById("convert-markdown").disabled = false;
    });

    // Markdown文字列をHTML文字列に変換する
    function convert() {
        const inputElement = document.getElementById('input');
        const outputElement = document.getElementById('output');

        const inputText = inputElement.value;

        // wasmのmarkdownToHtmlを呼び出している
        const outputText = markdownToHtml(inputText)

        outputElement.innerHTML = outputText;

    }

</script>
<div>
    <textarea id="input" placeholder="Enter text" rows="5" cols="50"></textarea>
    <button onClick="convert();" id="convert-markdown" disabled>Convert Markdown</button>
</div>
<div>
    <p>Output:
    </p>
        <span id="output">
        </span>
</div>

</body>

</html>

index.htmlをローカルサーバーを起動して読み込みます。
手段はなんでも良いですが、VSCodeのLive Serverを使うのが最も手軽だと思います。

動作確認

Markdownのテキストを入力してボタンを押すと...

click

無事HTMLテキストが表示されました。やったぜ。

view

さいごに

あんまり細かいことはわかってないですが、使い方を練習できたので満足です。

参考

Discussion