WASMをNext.jsで動かす
先日Scrapbox記法をMarkdownに変換するライブラリをRustで書いたという記事を書いた。
このライブラリは当初CLIとして使うことを想定していた。しかしせっかくRustで書いたのでWASMを生成してフロントエンドから動かせるようにしてみた。できたのは下記のページ。
Scrapboxのテキストを入力して変換するボタンを押すとMarkdownが生成される。
今回はこれを作る過程でNext.jsでWASMを動かすというのができたので大まかなやり方を書き残しておく。ざっくりとした手順としては下記。
- WASMファイルをNext.js内に配置
-
next.config.js
の設定 - dynamic importを使ってWASMを読み込む
実際にやってみるとわかるがとても簡単。
WASMファイルの配置
Rustからwasmの生成にはwasm-packを使った。これを使うとbg.wasm
とかbg.wasm.d.ts
などのファイルが生成される。RustからWASMを生成する方法についてはCompiling from Rust to WebAssembly - WebAssembly | MDNあたりの記事を読んでみると良い。
wasm-packでwasm-pack build --target web --release
をして生成したファイル群は下記のようになっているはず。
├── package.json
├── sb2md_converter.d.ts
├── sb2md_converter.js
├── sb2md_converter_bg.wasm
└── sb2md_converter_bg.wasm.d.ts
これら全てをNext.jsのソースコードのルートに配置する。自分の場合はsrc/
の中にresources/wasm/
というディレクトリを作ってファイルを丸ごと入れた。こんな感じ→https://github.com/YuheiNakasaka/personal-site/tree/master/src/resources/wasm
next.config.jsの設定
次にwebpackの設定。webpack5から導入された実験的機能の中にWASMファイルを同梱してくれるオプションがあるのでそれを使う。
next.config.jsにはこんな感じで書いておけばOK。
module.exports = {
webpack: (config, { isServer }) => {
config.experiments = {
asyncWebAssembly: true,
layers: true,
};
config.output.webassemblyModuleFilename = (isServer ? "../" : "") + "static/wasm/[modulehash].wasm";
return config;
}
}
dynamic import
実際にWASMを読み込んで呼び出して使ってみる。これにはいくつかのやり方があると思うが、dynamic importを使う方法が1番楽そうだった。
import { NextPage } from "next";
import { useCallback, useEffect } from "react";
import dynamic from "next/dynamic.js";
const WasmSample = dynamic({
loader: async () => {
const initWasm = await import("../../resources/wasm");
const { Greeting } = initWasm;
const onClick = useCallback(async () => {
const result = await Greeting.hello()
console.log(result)
}, []);
useEffect(() => {
initWasm.default();
}, [])
return () => {
return (
<button onClick={onClick}>実行する</button>
)
}
}
}, { ssr: false })
const Example: NextPage = () => {
return <WasmSample />
}
dynamic importで{ ssr: false }
を設定するのがポイント。
あとwasmファイルの中身にもよるんだけど上記の例はGreeting
structにhello
メソッドを生やしたRustのコードをWASM化した場合の例を書いてみてる。細かい部分は元のRustファイル側の実装による。なのであくまで参考までに。
ちなみにScrapboxのテキストをMarkdownに変換するWASMを実行してるコンポーネントのコードの例は下記。大枠は同じ。
personal-site/sb2md.tsx at master · YuheiNakasaka/personal-site
まとめ
Next.jsでWASMファイルを読み込んで呼び出して使う方法を書いた。一度動かせるようになってしまえば今後は雑にRustでコードを書きWASMを作ってフロントエンドから気軽に使えるようになるはず。
目下の課題としては生成されるWASMファイルが機能の割にデカすぎるという問題があったり、V8が強力すぎてWASMの出番ない?みたいなレベルの処理もあったりするから何でもかんでも本番導入できるという感じではない。
今回作ったScrapbox to Markdownも計算量の多いようなものでもないからJSで書いても十分な速度は出る。
が、まぁ面白かったのでいいじゃんガハハというノリでやった。
Discussion