LoginSignup
8
1

More than 1 year has passed since last update.

RustとWebAssemblyでDOMを操作する

Last updated at Posted at 2022-12-13

概要

WebAssemblyを使用して、HTMLのDOMを操作することを目的とします。
メモベースで書いていきます。

まずはプロジェクト作成

cargo newでライブラリのテンプレートを作成します。

$ cargo new --lib dom

Cargo.tomlに追記

多言語からもビルド後のライブラリを触れるようにするために、 carte-typecdylib
を設定します。

[lib]
crate-type = ["cdylib"]

WebAssemblyとJavaScript間でデータの受け渡しができるように、wasm-bindgen をインストールします。

[dependencies]
wasm-bindgen = "0.2.83"

ビルドを高速にするためにweb-sysのAPIを制限しているっぽいです。なので、dependencies.web-sysfeaturesとしてリストする必要があります。

To keep build times super speedy, web-sys gates each Web interface behind a cargo feature. Find the type or method you want to use in the API documentation; it will list the features that must be enabled to access that API.

以下のような感じ

[dependencies.web-sys]
version = "0.3.4"
features = [
  'Document',
  'Element',
  'HtmlElement',
  'Node',
  'Window',
]

他のAPIを追加したい場合は以下を参照して探すと良さそうです。

Rustのコード

use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
pub fn run() -> Result<(), JsValue> {
    // Use `web_sys`'s global `window` function to get a handle on the global
    // window object.
    let window = web_sys::window().expect("no global `window` exists");
    let document = window.document().expect("should have a document on window");
    let body = document.body().expect("document should have a body");

    // Manufacture the element we're gonna append
    let val = document.create_element("p")?;
    val.set_text_content(Some("Hello from Rust!"));

    body.append_child(&val)?;

    Ok(())
}

expectは実行の失敗時に表示される文字列を引数とするメソッドです。

document.create_elementpタグを作成し、Hello from Rust! をセットしています。

val.set_text_contentの引数で使用しているSome(T)は何かしらの型を1つ持った、匿名タプル構造体型です。

コード的には意外とJavaScriptでの書き方に似ているのでわかりやすいですね。

JavaScriptのプロジェクトを作成

webpack-dev-serverで起動するための準備をします。

package.json
{
  "scripts": {
    "build": "webpack",
    "serve" : "webpack serve"
  },
  "devDependencies": {
    "@wasm-tool/wasm-pack-plugin": "1.5.0",
    "text-encoding": "^0.7.0",
    "html-webpack-plugin": "^5.3.2",
    "webpack": "^5.49.0",
    "webpack-cli": "^4.7.2",
    "webpack-dev-server": "^3.11.2"
  }
}
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");

module.exports = {
    entry: './index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'index.js',
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: 'index.html'
        }),
        new WasmPackPlugin({
            crateDirectory: path.resolve(__dirname, ".")
        }),
        // Have this example work in Edge which doesn't ship `TextEncoder` or
        // `TextDecoder` at this time.
        new webpack.ProvidePlugin({
          TextDecoder: ['text-encoding', 'TextDecoder'],
          TextEncoder: ['text-encoding', 'TextEncoder']
        })
    ],
    mode: 'development',
    experiments: {
        asyncWebAssembly: true
   }
};

HtmlWebpackPlugin

HtmlWebpackPluginは、バンドルしたjsファイルをsourceにしたscriptタグを挿入してくれます。

WasmPackPlugin

WasmPackPluginは、Rustで書かれたlibテンプレートをpkgディレクトリにビルドしてくれる設定してくれます。

index.html
<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
  </head>
  <body>
  </body>
</html>
index.js
import('./pkg')
  .catch(console.error);

これで準備が整いました。

起動

npm run serve でwebpack-dev-serverを起動します。
localhost:8080にアクセスするとtemplateのindex.htmlに対してrustで書いたwasm用のコードのpタグのappendがされて Hello from Rust!という表示がでました。
スクリーンショット 2022-12-13 1.43.05.png

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1