ルーター (Router)
シングルページアプリケーション (SPA) のルーターは、URL に基づいて異なるページを表示する処理を行います。リンクをクリックしたときに異なるリモートリソースを要求するデフォルトの動作とは異なり、ルーターはアプリケーション内の有効なルートを指すようにローカルで URL を設定します。その後、ルーターはこの変更を検出し、レンダリングする内容を決定します。
Yew は yew-router
クレートでルーターサポートを提供します。使用を開始するには、依存関係を Cargo.toml
ファイルに追加してください。
yew-router = { git = "https://github.com/yewstack/yew.git" }
必要なツールはすべて yew_router::prelude
モジュールで提供されています。
使用方法
まず、Route
を定義する必要があります。
ルートは Routable
を派生する enum
で定義されます。この列挙型は Clone + PartialEq
を実装する必要があります。
use yew_router::prelude::*;
#[derive(Clone, Routable, PartialEq)]
enum Route {
#[at("/")]
Home,
#[at("/secure")]
Secure,
#[not_found]
#[at("/404")]
NotFound,
}
Route
と <Switch />
コンポーネントはペアで使用され、後者はブラウザの現在の URL に一致するパスのバリアントを見つけ、それを render
コールバックに渡します。その後、コールバックがレンダリングする内容を決定します。パスが一致しない場合、ルーターは not_found
属性を持つパスにナビゲートします。指定されたルートがない場合、何もレンダリングされず、一致するルートがないことを示すメッセージがコンソールに記録されます。
yew-router のほとんどのコンポーネント、特に <Link />
と <Switch />
は、ある Router コンポーネント(例: <BrowserRouter />
)の(深い)子要素である必要があります。通常、アプリケーションには 1 つの Router しか必要なく、通常は最上位の <App />
コンポーネントによって直ちにレンダリングされます。Router はコンテキストを登録し、これは Links と Switches の機能に必要です。以下に例を示します。
ブラウザ環境で yew-router
を使用する場合、<BrowserRouter />
を強く推奨します。他のルータータイプについては API リファレンス を参照してください。
use yew_router::prelude::*;
use yew::prelude::*;
#[derive(Clone, Routable, PartialEq)]
enum Route {
#[at("/")]
Home,
#[at("/secure")]
Secure,
#[not_found]
#[at("/404")]
NotFound,
}
#[function_component(Secure)]
fn secure() -> Html {
let navigator = use_navigator().unwrap();
let onclick = Callback::from(move |_| navigator.push(&Route::Home));
html! {
<div>
<h1>{ "Secure" }</h1>
<button {onclick}>{ "Go Home" }</button>
</div>
}
}
fn switch(routes: Route) -> Html {
match routes {
Route::Home => html! { <h1>{ "Home" }</h1> },
Route::Secure => html! {
<Secure />
},
Route::NotFound => html! { <h1>{ "404" }</h1> },
}
}
#[function_component(Main)]
fn app() -> Html {
html! {
<BrowserRouter>
<Switch<Route> render={switch} /> // <- must be child of <BrowserRouter>
</BrowserRouter>
}
}
パスセグメント
ルーターは、動的および名前付きワイルドカードセグメントを使用してルートから情報を抽出することもできます。次に、<Switch />
内で投稿の ID にアクセスし、それを適切なコンポーネントにプロパティとして渡すことができます。
use yew::prelude::*;
use yew_router::prelude::*;
#[derive(Clone, Routable, PartialEq)]
enum Route {
#[at("/")]
Home,
#[at("/post/:id")]
Post { id: String },
#[at("/*path")]
Misc { path: String },
}
fn switch(route: Route) -> Html {
match route {
Route::Home => html! { <h1>{ "Home" }</h1> },
Route::Post { id } => html! {<p>{format!("You are looking at Post {}", id)}</p>},
Route::Misc { path } => html! {<p>{format!("Matched some other path: {}", path)}</p>},
}
}
Post {id: String}
の代わりに通常の Post
バリアントを使用することもできます。例えば、Post
が別のルーターと一緒にレンダリングされる場合、そのフィールドは冗長になる可能性があります。詳細については、以下のネストされたルーターセクションを参照してください。
フィールドは Route
列挙型の一部として Clone + PartialEq
を実装する必要があることに注意してください。また、シリアル化と逆シリアル化のために std::fmt::Display
と std::str::FromStr
を実装する必要があります。整数、浮動小数点数、および文字列などのプリミティブ型はこれらの要件を既に満たしています。
パスの形式が一致しても、逆シリアル化が失敗した場合(FromStr
に基づく)、ルーターはルートが一致しないと見なし、見つからないルートをレンダリングしようとします(または、見つからないルートが指定されていない場合は空白ページをレンダリングします)。
以下の例を参照してください:
#[derive(Clone, Routable, PartialEq)]
enum Route {
#[at("/news/:id")]
News { id: u8 },
#[not_found]
#[at("/404")]
NotFound,
}
// switch 関数は News と id をレンダリングします。ここでは省略されています。
セグメントが 255 を超えると、u8::from_str()
は失敗し、ParseIntError
を返します。この場合、ルーターはルートが一致しないと見なします。
ルーティング構文やパラメータのバインディング方法の詳細については、route-recognizer を参照してください。
位置 (Location)
ルーターはコンテキストを介して一般的な Location
構造体を提供し、ルート情報にアクセスするために使用できます。これらはフ ックまたは ctx.link()
上の便利な関数を介して取得できます。
ナビゲーション
yew_router
はナビゲーションを処理するためのいくつかのツールを提供します。
リンク
<Link />
は <a>
要素としてレンダリングされ、onclick
イベントハンドラは preventDefault を呼び出し、ターゲットページを履歴にプッシュして必要なページをレンダリングします。これはシングルページアプリケーションに期待される動作です。通常のアンカー要素のデフォルトの onclick
はページをリロードします。
<Link />
コンポーネントはその子要素を <a>
要素に渡します。これはアプリ内ルーティングのための <a/>
の代替として考えることができます。違いは、href
の代わりに to
属性を提供する必要があることです。使用例は以下の通りです:
<Link<Route> to={Route::Home}>{ "click here to go home" }</Link<Route>>
構造体変数も正常に動作します:
<Link<Route> to={Route::Post { id: "new-yew-release".to_string() }}>{ "Yew!" }</Link<Route>>
ナビゲーションインターフェース
ナビゲーター API は、関数コンポーネントと構造体コンポーネントの両方で提供されます。これにより、コールバックがルートを変更できるようになります。どちらの場合でも、Navigator
インスタンスを取得してルートを操作できます。
関数コンポーネント
関数コンポーネントの場合、基礎となるナビゲータープロバイダーが変更されると、use_navigator
フックはコンポーネントを再レンダリングします。
以下は、クリック時に Home
ルートにナビゲートするボタンを実装する例です。
#[function_component(MyComponent)]
pub fn my_component() -> Html {
let navigator = use_navigator().unwrap();
let onclick = Callback::from(move |_| navigator.push(&Route::Home));
html! {
<>
<button {onclick}>{"Click to go home"}</button>
</>
}
}
ここでの例では Callback::from
を使用しています。ターゲットルートがコンポーネントのルートと同じになる可能性がある場合、または安全のために、通常のコールバックを使用してください。例えば、各ページにロゴボタンがあり、そのボタンをクリックするとホームに戻るとします。ホームページでそのボタンを2回クリックすると、同じHomeルートがプッシュされ、use_navigator
フックが再レンダリングをトリガーしないため、コードがクラッシュします。
現在の位置をスタックに新しい位置としてプッシュするのではなく置き換えたい場合は、navigator.push()
の代わりに navigator.replace()
を使用してください。
navigator
はコールバックに移動する必要があるため、他のコールバックで再利用できないことに気付くかもしれません。幸いなことに、navigator
は Clone
を実装しているため、異なるルートに対して複数のボタンを設定する方法は次のとおりです:
use yew::prelude::*;
use yew_router::prelude::*;
#[function_component(NavItems)]
pub fn nav_items() -> Html {
let navigator = use_navigator().unwrap();
let go_home_button = {
let navigator = navigator.clone();
let onclick = Callback::from(move |_| navigator.push(&Route::Home));
html! {
<button {onclick}>{"click to go home"}</button>
}
};
let go_to_first_post_button = {
let navigator = navigator.clone();
let onclick = Callback::from(move |_| navigator.push(&Route::Post { id: "first-post".to_string() }));
html! {
<button {onclick}>{"click to go the first post"}</button>
}
};
let go_to_secure_button = {
let onclick = Callback::from(move |_| navigator.push(&Route::Secure));
html! {
<button {onclick}>{"click to go to secure"}</button>
}
};
html! {
<>
{go_home_button}
{go_to_first_post_button}
{go_to_secure_button}
</>
}
}