LoginSignup
26
10

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-09-10

概要

Rustをwasmにコンパイルすることで利用可能なフロントエンドフレームワークを作ってみました。なお、フロントエンド用のクレートだとYewというものがすでに存在しています。

c.f. Yew

インストール

ドキュメントコメントを全然付けていないのと開発途上も良いところなので、まだCrates.ioには公開していません。githubからcloneして使うことになります。

Crates.ioに公開しました。

Kaguraのgithubリポジトリ

使い方

kagura::Component::new(initial_state, update, render)によってコンポーネントを作ってkagura::run(component, id)でエントリーポイントのidとルートコンポーネントを指定すれば良いです。

hello world の例を示します。

extern crate kagura;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

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

struct State;

struct Msg;

fn update(_: &mut State, _: &Msg) {}

fn render(_: &State) -> kagura::Html<Msg> {
    use kagura::Html;
    use kagura::Attributes;
    use kagura::Events;
    Html::h1(
        Attributes::new(),
        Events::new(),
        vec![
            Html::unsafe_text("hello kagura"),
        ],
    )
}

Stateがコンポーネントの状態を表現し、Msgがイベントをupdateにバインドします。updateMsgをもとにStateを更新し、Stateに基づきrenderでビューを作るという構造を取ります。(要はTEA)

あくまでコンポーネント指向なので、コンポーネントのレンダーからさらに別のコンポーネントを呼び出すことができます。呼び出されたコンポーネントのステートは呼び出し元のステートが変わらない限りリセットされません。

現状、子コンポーネントから親コンポーネントにイベントを伝搬するまともな方法が無いので、あまりコンポーネント指向としての意味を成していません。(今後対応します)

subscribeで親コンポーネントにメッセージを伝搬できるようになりました。

以下に子コンポーネントを使用する例を示します。

extern crate kagura;
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

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

struct State;

struct Msg;

fn update(_: &mut State, _: &Msg) {}

fn render(_: &State) -> kagura::Html<Msg> {
    use kagura::Html;
    use kagura::Attributes;
    use kagura::Events;
    Html::div(
        Attributes::new()
            .with_style("color: red;"),
        Events::new(),
        vec![
            Html::component(child::new()),
            Html::component(child::new()),
            Html::component(child::new()),
            Html::component(child::new()),
        ],
    )
}

mod child {
    pub fn new() -> kagura::Component<Msg, State> {
        kagura::Component::new(0, update, render)
    }

    pub type State = u64;

    pub enum Msg {
        CountUp,
    }

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

    fn render(state: &State) -> kagura::Html<Msg> {
        use kagura::Events;
        use kagura::Attributes;
        use kagura::Html;
        Html::h1(
            Attributes::new(),
            Events::new()
                .with_on_click(|| {Msg::CountUp}),
            vec![Html::unsafe_text(state.to_string())],
        )
    }
}

onClickイベントは() -> MsgなクロージャでMsgにマップしています。もし、子コンポーネントの初期状態を親コンポーネントから指定したければ、child::new() の引数を用いることになります。

実装状況

現状、タグ・属性の一部とonClickイベントにしか対応できていません。また、仮想DOMの差分レンダリングをコンポーネント単位で行っているため無駄が多いです。

今後の予定

  1. タグ・属性・イベントの対応数を増やす
  2. 仮想DOMのレンダリングを高速化(せめてタグ単位で差分を取る)
26
10
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
26
10