Help us understand the problem. What is going on with this article?

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

概要

ボタンを押すと、数字がカウントアップされていき、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.3.3"
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(0, update, render), "app");
}

type State = u64;

enum Msg {
    CountUp,
}

struct Sub;

fn update(state: &mut State, msg: &Msg) -> Option<Sub> {
    match msg {
        Msg::CountUp => {
            *state += 1;
        }
    }
    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でフロントエンドフレームワークを作ってみた。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away