LoginSignup
27
26

More than 3 years have passed since last update.

[Kagura] Kagura + Rust でWebページを作成

Last updated at Posted at 2019-09-12

概要

ボタンを押すと、数字がカウントアップされていき、3の倍数の時は数字の代わりに青文字でFizzと、5の倍数の時は数字の代わりに赤背景でBuzzと、3と5両方の倍数の時は数字の代わりに赤背景に青文字でFizzBuzzと表示するWebアプリケーションを作ります。

準備

wasm-pack をインストール

※cargoが必要です

$ cargo install wasm-pack

クレートを作成

$ cargo new --lib fizzbuzz
$ cd fizzbuzz

この時点でのディレクトリ構成(一部省略):

fizzbuzz
 ├cargo.toml
 └src
  └lib.rs

webpack等のインストール

※npmが必要です
※yarn等を使う場合は適宜読み替えてください

$ npm init
$ npm install -D @wasm-tool/wasm-pack-plugin html-webpack-plugin webpack webpack-cli webpack-dev-server

この時点でのディレクトリ構成(一部省略):

fizzbuzz
 ├cargo.toml
 ├package.json
 ├package-lock.json
 └src
  └lib.rs

webpack.config.js の作成

クレートのルートディレクトリに webpack.config.js というファイルを作り、以下のように記述します。

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");

module.exports = {
    resolve: {
        extensions: [".js"]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: path.join(__dirname, "./src/index.html")
        }),
        new WasmPackPlugin({
            crateDirectory: path.join(__dirname, "./")
        })
    ]
};

この時点でのディレクトリ構成(一部省略):

fizzbuzz
 ├cargo.toml
 ├package.json
 ├package-lock.json
 ├webpack.config.js
 └src
  └lib.rs

その他、必要なファイルの作成

fizzbazz/src に index.jsindex.html を作成し、それぞれ以下のように記述します。

index.js:

import("../pkg");

index.html:

<!DOCTYPE html>
<html>

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

<body>
    <div id="app"></div>
</body>

</html>

この時点でのディレクトリ構成(一部省略):

fizzbuzz
 ├cargo.toml
 ├package.json
 ├package-lock.json
 ├webpack.config.js
 └src
  ├index.html
  ├index.js
  └lib.rs

その他、設定の作成

package.jsonscripts"start": "./node_modules/.bin/webpack-dev-server" を追加します。

この時点での package.json の例:

{
  "name": "fizzbuzz",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "./node_modules/.bin/webpack-dev-server"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@wasm-tool/wasm-pack-plugin": "^1.0.1",
    "html-webpack-plugin": "^3.2.0",
    "webpack": "^4.39.3",
    "webpack-cli": "^3.3.8",
    "webpack-dev-server": "^3.8.0"
  }
}

cargo.toml[dependencies]kagura = "0.1.3" と wasm-bindgen = "0.2" を、[lib]crate-type = ["cdylib", "rlib"] を追加します。

この時点での cargo.toml の例:

[package]
name = "fizzbuzz"
version = "0.1.0"
authors = ["****************"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

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

[dependencies]
kagura = "0.8.6"
wasm-bindgen = "0.2"

アプリケーションの作成

Kagura について

Kagura はWebフロントエンド用のフレームワークです。

kagura::Component::new() によりコンポーネントを作成し、そのコンポーネントとエントリーポイントの id を kagura::run() に渡すとWebアプリケーションが実行されます。

ソースコード

lib.rs:

extern crate kagura;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen(start)]
pub fn main() {
    kagura::run(kagura::Component::new(init, update, render), "app");
}

type State = u64;

enum Msg {
    CountUp,
}

struct Sub;

fn init() -> (State, Cmd<Msg, Sub>) {
    (0, Cmd::none())
}

fn update(state: &mut State, msg: &Msg) -> Cmd<Msg, Sub> {
    match msg {
        Msg::CountUp => {
            *state += 1;
        }
    }
    Cmd::none()
}

fn render(state: &State) -> kagura::Html<Msg> {
    use kagura::Attributes;
    use kagura::Events;
    use kagura::Html;

    let text = if state % 15 == 0 {
        "fizzbuzz".to_string()
    } else if state % 5 == 0 {
        "buzz".to_string()
    } else if state % 3 == 0 {
        "fizz".to_string()
    } else {
        state.to_string()
    };

    let color = if state % 3 == 0 { "#00f" } else { "#000" };

    let bg_color = if state % 5 == 0 { "#f00" } else { "#fff" };

    Html::button(
        Attributes::new()
            .style("color", color)
            .style("background-color", bg_color)
            .style("width", "10ch")
            .style("height", "2rem"),
        Events::new().on_click(|_| Msg::CountUp),
        vec![Html::unsafe_text(text)],
    )
}

実行方法

$ npm start

localhost:8080 でページを見られます。

実行結果例

fizzbuzz.PNG

解説

main()

kagura::run() でアプリケーションを開始しています。kagura::Component::new でルートコンポーネントとなるコンポーネントを作成し、エントリーポイントのIDを app としています。

kagura::Component::new() は第1引数に初期状態、第2引数にupdate、第3引数にrenderを取ります。updateは状態とメッセージを受け取り、状態を更新する関数へのポインタです。renderはメッセージを受け取りHtmlを返す関数へのポインタです。

State Msg Sub

状態を表す型を type State = u64 として定義しています。当然ながら、名前は State でなくとも、統一されていればコンパイルは通ります。

同様にメッセージを表す型を Msg としています。 Sub は今回は使いませんが update の戻り値で必要になるので定義しておきました。Sub は複数のコンポーネントを組み合わせて使うときに必要になります。

update

状態の更新方法を記述しています。今回は Msg::CountUp メッセージを受け取ると状態を1増やします。

render

状態の表示方法を記述しています。イベントにはイベントをメッセージにバインドするクロージャを記述します。

For your information

[Kagura] Rustでフロントエンドフレームワークを作ってみた。

27
26
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
27
26