🦀
vite (react.ts) で wasmを動かす
対象
- WebAssemblyに興味があり、簡単なサンプルを動かしてみたい方
環境
$ sw_vers
ProductName: macOS
ProductVersion: 11.4
BuildVersion: 20F71
$ cargo --version
cargo 1.53.0 (4369396ce 2021-04-27)
$ yarn --version
1.22.10
$ wasm-pack --version
wasm-pack 0.10.1
viteでプロジェクトの準備
先にviteでプロジェクトを作り、その中にwasm用のプロジェクトを作ります。
$ yarn create vite
$ cd <project_name>
$ yarn
$ yarn dev
3000ポートで以下を確認できます。
wasmの準備
テンプレートを利用します。
プロジェクト名はわかりやすいようにwasmにしてます。
$ cargo generate --git https://github.com/rustwasm/wasm-pack-template
Project Name: wasm
現時点でのプロジェクト構成はこんな感じになってます。
$ ls
index.html package.json tsconfig.json wasm
node_modules src vite.config.ts yarn.lock
wasmのビルド
wasm-packでビルドします。
未インストールなら、以下でインストールしておきます。
$ cargo install wasm-pack
デフォルトだと、webpack用にビルドされてしまうので、targetでwebを指定します。
cf. https://github.com/rustwasm/wasm-pack/issues/790#issuecomment-583893485
$ wasm-pack build --target web
warningで以下のような警告が出るので、set_panic_hook
は外しておいて良いかもしれません。
warning: function is never used: `set_panic_hook`
これで、wasm/pkg配下にwasmがビルドされました。
viteでwasmを使う
App.tsxを修正します。
src/App.tsx
import React, {useEffect, useState} from 'react'
import logo from './logo.svg'
import './App.css'
+ import init, { greet } from '../wasm/pkg'
function App() {
const [count, setCount] = useState(0)
+ useEffect(() => {
+ init()
+ }, [])
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>Hello Vite + React!</p>
<p>
- <button type="button" onClick={() => setCount((count) => count + 1)}>
+ <button type="button" onClick={() => greet()}>
count is: {count}
</button>
</p>
<p>
Edit <code>App.tsx</code> and save to test HMR updates.
</p>
<p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
{' | '}
<a
className="App-link"
href="https://vitejs.dev/guide/features.html"
target="_blank"
rel="noopener noreferrer"
>
Vite Docs
</a>
</p>
</header>
</div>
)
}
export default App
ボタンをクリックすることで、動作確認できます。
他の例
rust-cookbookの一番最初にある generate-random-numbersをwasm化してみます。
rustのコード追加
generate-random-numbersのコードを参考にして、以下のように追加します。
wasm/src/lib.rs
mod utils;
use wasm_bindgen::prelude::*;
+ use rand::Rng;
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn greet() {
alert("Hello, wasm!");
}
+ #[wasm_bindgen]
+ pub fn gen_rand_num() {
+ let mut rng = rand::thread_rng();
+
+ let n1: u8 = rng.gen();
+ alert(&format!("Random u8: {}", n1));
+ }
Cargo.tomlにrandを追加しておきます。
wasm/Cargo.toml
[package]
name = "wasm"
version = "0.1.0"
authors = ["pilefort <pilefort2020@gmail.com>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
wasm-bindgen = "0.2.63"
+ rand = "0.8.4"
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"
この状態でビルドすると、以下のエラーが出るので、エラーの指示に従います。
$ wasm-pack build --target web
error: the wasm32-unknown-unknown target is not supported by default, you may need to enable the "js" feature. For more information see: https://docs.rs/getrandom/#webassembly-support
wasm/Cargo.toml
[dependencies]
wasm-bindgen = "0.2.63"
rand = "0.8.4"
+ getrandom = { version = "0.2", features = ["js"] }
もう一度ビルドすると、ビルドに成功します。
$ wasm-pack build --target web
...
[INFO]: ✨ Done in 5.70s
[INFO]: 📦 Your wasm pkg is ready to publish at /Users/<user_name>/<project_name>/wasm/pkg.
src/App.tsxの修正
src/App.tsx
- import init, { greet } from '../wasm/pkg'
+ import init, { gen_rand_num } from '../wasm/pkg'
...
- <button type="button" onClick={() => greet()}>
+ <button type="button" onClick={() => gen_rand_num()}>
動作確認
このような感じでランダムな数値を出せるようになりました。
サンプルコード
以上
Discussion