以下の内容はhttps://haibara-works.hatenablog.com/entry/2020/05/07/011736より取得しました。


Vue Router環境でWebAssemblyを使う

はじめに

Vue CLI + Vue Router + Vuetifyで構築したSPAでWebAssembly(以下Wasm)を使おうとして,少し詰まったのでメモしておきます.

Hello Wasm

Wasm事始めということで,こちらの記事にある,Wasmのサンプルを動かそうとしました.
qiita.com

下記のリポジトリでHTMLコードとWasmバイナリが公開されています.
github.com

このリポジトリからindex.htmlとsample.wasmをダウンロードして,プロジェクト内の public/ 配下にこれらのファイルを置きます(下図).
(簡単のため,下記レポジトリ内の index.hrml は count.html,sample.wasmはcount.wasmとリネームしました.)

public/
      ├ count.html
      └ count.wasm

その後 yarn serve して,ブラウザからlocalhost:8080/count.html にアクセスすると,期待通りの画面が表示され,正常に動作します.

謎のコンパイルエラー

さて,HTML + Wasmの動作を確認できたので,次はVueに移植していきます.
ディレクトリ構想は下図のようにしました.

src/
   └ views/
          └ count/
                 ├ count.vue
                 └ count.wasm

count.vueは先ほどのcount.htmlをVueに移植したもので,次のようにしました.

  • count.vue
<template>
  <v-app id>
    <v-content>
      <v-content>
        <v-row justify="space-around" no-gutters>
          <h1>wasm-count</h1>
        </v-row>
        <v-row justify="space-around" no-gutters>
          <v-btn id="countup">count</v-btn>
          <h1>{{ data.val }}</h1>
        </v-row>
      </v-content>
    </v-content>
  </v-app>
</template>

<script>
const wasm_file_path = "count.wasm";

export default {
  data: () => ({
    data: {
      val: "-"
    }
  }),
  methods: {
    main(data) {
      console.log("file: " + wasm_file_path);

      fetch(wasm_file_path)
        .then(function(response) {
          return response.arrayBuffer();
        })
        .then(function(buffer) {
          return WebAssembly.compile(buffer);
        })
        .then(function(module) {
          return WebAssembly.instantiate(module);
        })
        .then(function(instance) {
          document.getElementById("countup").addEventListener(
            "click",
            function() {
              data.val = instance.exports.count();
            },
            false
          );
        });
    }
  },
  mounted() {
    this.main(this.data);
  }
};
</script>

細かい処理は置いておいて,wasm_file_path で指定したWasmバイナリを fetch で取ってきて処理する,というのが大まかな流れです.
Vue Routerで /wasm/count でアクセスできるように設定したうえで,yarn serveをして,ブラウザで localhost:8080/#/wasm/count にアクセスします.
すると,次のようなエラーが表示されました.

f:id:w_haibara:20200507004928p:plain
localhostでのエラー

Uncaught (in promise) CompileError: WebAssembly.compile(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0

「Wasmバイナリの先頭には 00 61 73 6d というマジックナンバーがあるはずなのに,指定されたファイルの先頭は 3c 21 44 4f ですよ」というエラーが表示されている.
さっきはちゃんと動いたバイナリなのに何故??と思い,バイナリエディタで確認してみると当該ファイルの先頭はしっかり「00 61 73 6d」となっていました.

ぐぬぬと思いつつ,localhost固有の原因かもしれないと思い,Github Pagesにデプロイしてみました.
ページにアクセスすると,今度は以下のようなエラーが表示されました.

f:id:w_haibara:20200507004829p:plain
Github Pagesでのエラー
先ほどのエラーも同様に表示されていますが,それより前に404エラーが表示されています!

また,次のissueも発見しました.
github.com
先ほどの

Uncaught (in promise) CompileError: WebAssembly.compile(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0

というエラーが話題に上がっています.
Q. なんかこんなエラー出たんだけど.
A. 「3c 21 44 4f」ってASCIIで読んだら「<!DO」じゃん.多分404ページかなんかを読んでるよ,それ.

なるほど!!!
fetch(wasm_file_path)をした際に,404ページが返ってきて,HTMLの1行目「<!DOCTYPE html>」を読み込み,上記のエラーが出現したようです.

解決

要はHTTPでcount.wasmにアクセスできるようにしてやればいいわけですね.
解決法は他にもあるかもしれませんが,今回は public/ 配下にWasmバイナリを配置することで解決しました.

public/
      └ wasm/
            └ count.wasm

これに伴って,Vueファイルでは

const wasm_file_path = "wasm/count.wasm";

とファイル名を定めることにしました.
ということで,無事にVue製SPA内でWasmを動かすことができました!
これは https://w-haibara.github.io/wasm/count で確認することができます.

おわりに

意外なところで詰まってしまいましたが,なんとかWasmを動かせました.
これでVue.js + Wasmで遊べます.




以上の内容はhttps://haibara-works.hatenablog.com/entry/2020/05/07/011736より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14