Tauri+Sycamoreで、sycamore-routerを使って画面遷移する

前回はTauri+SycamoreでYAMLの読み書きを実装しました。 今回は画面遷移を実装してみます。

前回はこちら
a3colorr.hatenablog.com

基本的には公式ドキュメントのRoutingページをなぞれば実現できるのですが、一部の説明とサンプルコードに誤りがあるようで、コピペではエラーになります。
このブログ本文中ではその部分を補完しています。
!! 2023-05-20現在、公式ドキュメントの当該サンプルコードは誤記訂正されています

公式ドキュメントのRoutingページ
https://sycamore-rs.netlify.app/docs/advanced/routing#using-router

環境

やりたいこと

2つのページ(Home、About)を用意して、相互にページ遷移する

準備

プロジェクトルートのCargo.tomlsycamore-routerの依存関係を追加します。
公式ドキュメントでは0.9.0-beta.1を指定していますが、crate.ioを見ると最新のメジャーバージョンが0.8.0だったので、今回は0.8.0としました。

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde-wasm-bindgen = "0.4.3"
js-sys = "0.3.59" 
serde = { version = "1.0.140", features = ["derive"] }
wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] }
wasm-bindgen-futures = "0.4.32"
sycamore = { version = "0.8", features = ["suspense"] }
sycamore-router = "0.8.0" <-これ

crate.ioのページ
https://crates.io/crates/sycamore-router

ちなみに、0.9.0-beta.1にすると以下の型不一致エラーが出ます。

expected struct `sycamore::prelude::View`, found struct `sycamore_core::view::View

betaだからまた変更が入るかもしれないから気にしなくても良いんだろうか?、将来的には今動いているコードを変えないとダメなんだろうな、などと色々思うところがありました。

実装する

最初に、プロジェクトルートのsrc/app.rsでモジュールをインポートします。

// 公式ドキュメントでは以下を指定しているが、1つ不足している。
// use sycamore_router::{Route, Router, RouterProps};

// 正しくは次の通り。
// `RouterProps`は今回使用しないので、外しても良い。
use sycamore_router::{Route, Router, HistoryIntegration, RouterProps};

次に、ルーティング用のenumを追加します。

#[derive(Route)]
enum AppRoutes {
    #[to("/")]
    Home,
    #[to("/about")]
    About,
    #[not_found]
    NotFound
}

各値(バリアント)には、#[to(_)]属性または#[not_found]属性のいずれかを指定します。
例えば、Homeページ(ルートページ)には#[to("/")]About ページ(サブページ)には#[to("/about")]を指定します。
一方、#[not_found]は、他のすべてのルートが一致しない場合に遷移するルートに指定します。#[not_found]のルートが無いとコンパイルエラーになります。また、#[not_found]を指定できるルートは 1 つだけです。

最後に、ViewにRouterを追加します。
(この辺りは、ネストが深くて見づらくなります。。。)
公式ドキュメントに載っているRouterのサンプルコードは、いくつかのview!{}cxの指定が抜けているため、サンプルコードをコピペしてもコンパイルエラーになります。
!! 2023-05-20現在、公式ドキュメントの当該サンプルコードは誤記訂正されています

// 予期しない入力(引数に`cx`を渡してください)
unexpected end of input, expected `,` (help: make sure you pass the cx variable to the macro as an argument)

cxを追加した様子。

view! {cx,
    Router(
        integration = HistoryIntegration::new(),
        view=|cx, route: &ReadSignal<AppRoutes>| {
            view! {cx,
                div(class="app") {
                    (match route.get().as_ref() {
                        AppRoutes::Home => view! {cx,
                            main(class="container") {
                                "Home Page"
                                a(href="/about") {
                                    "to About"
                                }
                            }
                        },
                        AppRoutes::About => view! {cx,
                            main(class="container") {
                                "About Page"
                                div {
                                    a(href="/") {
                                        "to Home"
                                    }
                                }
                                div {
                                    a(href="/not-found") {
                                        "to NotFound"
                                    }
                                }
                            }
                        },
                        AppRoutes::NotFound => view! {cx,
                            main(class="container") {
                                "Not Found"
                                div {
                                    a(href="/") {
                                        "to Home"
                                    }
                                }
                            }
                        }
                    })
                }
            }
        }
    )
}

動作している様子

cargo tauri devで動作確認をします。
ビルドエラー等がなければ、次のように動作します。

sycamore-routeによる画面遷移