乐闻世界logo
搜索文章和话题

Yew 中如何实现路由管理,支持哪些路由功能?

2月19日 16:21

Yew 路由管理详解

Yew 提供了强大的路由管理功能,支持客户端路由、嵌套路由、路由守卫等高级特性。

基础路由配置

1. 安装依赖

toml
[dependencies] yew = { version = "0.21", features = ["csr"] } yew-router = { version = "0.18" }

2. 定义路由

rust
use yew::prelude::*; use yew_router::prelude::*; #[derive(Clone, Routable, PartialEq)] enum Route { #[at("/")] Home, #[at("/about")] About, #[at("/contact")] Contact, #[at("/users/:id")] User { id: u32 }, #[at("/posts/:post_id/comments/:comment_id")] PostComment { post_id: u32, comment_id: u32 }, #[not_found] #[at("/404")] NotFound, }

3. 创建路由组件

rust
#[function_component(Home)] fn home() -> Html { html! { <div> <h1>{ "Welcome Home" }</h1> <p>{ "This is the home page" }</p> </div> } } #[function_component(About)] fn about() -> Html { html! { <div> <h1>{ "About Us" }</h1> <p>{ "Learn more about our company" }</p> </div> } } #[function_component(Contact)] fn contact() -> Html { html! { <div> <h1>{ "Contact Us" }</h1> <p>{ "Get in touch with us" }</p> </div> } } #[derive(Properties, PartialEq)] pub struct UserProps { pub id: u32, } #[function_component(User)] fn user(props: &UserProps) -> Html { html! { <div> <h1>{ format!("User Profile: {}", props.id) }</h1> <p>{ "View user details" }</p> </div> } } #[derive(Properties, PartialEq)] pub struct PostCommentProps { pub post_id: u32, pub comment_id: u32, } #[function_component(PostComment)] fn post_comment(props: &PostCommentProps) -> Html { html! { <div> <h1>{ format!("Post {} - Comment {}", props.post_id, props.comment_id) }</h1> <p>{ "View comment details" }</p> </div> } } #[function_component(NotFound)] fn not_found() -> Html { html! { <div> <h1>{ "404 - Page Not Found" }</h1> <p>{ "The page you're looking for doesn't exist" }</p> </div> } }

4. 设置路由切换

rust
#[function_component(Switch)] fn switch() -> Html { let route = use_route::<Route>().unwrap(); match route { Route::Home => html! { <Home /> }, Route::About => html! { <About /> }, Route::Contact => html! { <Contact /> }, Route::User { id } => html! { <User id={*id} /> }, Route::PostComment { post_id, comment_id } => { html! { <PostComment post_id={*post_id} comment_id={*comment_id} /> } } Route::NotFound => html! { <NotFound /> }, } }

5. 创建应用组件

rust
#[function_component(App)] fn app() -> Html { html! { <BrowserRouter> <div class="app"> <nav class="navbar"> <Link<Route> to={Route::Home}>{ "Home" }</Link<Route>> <Link<Route> to={Route::About}>{ "About" }</Link<Route>> <Link<Route> to={Route::Contact}>{ "Contact" }</Link<Route>> <Link<Route> to={Route::User { id: 1 }}>{ "User 1" }</Link<Route>> </nav> <main class="content"> <Switch /> </main> </div> </BrowserRouter> } } fn main() { yew::Renderer::<App>::new().render(); }

高级路由功能

1. 嵌套路由

rust
#[derive(Clone, Routable, PartialEq)] enum Route { #[at("/")] Home, #[at("/dashboard")] Dashboard, #[at("/dashboard/settings")] DashboardSettings, #[at("/dashboard/profile")] DashboardProfile, } #[function_component(Dashboard)] fn dashboard() -> Html { html! { <div class="dashboard"> <aside class="sidebar"> <Link<Route> to={Route::Dashboard}>{ "Overview" }</Link<Route>> <Link<Route> to={Route::DashboardSettings}>{ "Settings" }</Link<Route>> <Link<Route> to={Route::DashboardProfile}>{ "Profile" }</Link<Route>> </aside> <main class="dashboard-content"> <Switch /> </main> </div> } }

2. 路由参数

rust
#[derive(Clone, Routable, PartialEq)] enum Route { #[at("/products/:category/:id")] Product { category: String, id: u32 }, } #[function_component(Product)] fn product() -> Html { let route = use_route::<Route>().unwrap(); if let Route::Product { category, id } = route { html! { <div> <h1>{ format!("{} - Product {}", category, id) }</h1> </div> } } else { html! { <div>{ "Invalid route" }</div> } } }

3. 查询参数

rust
use yew_router::hooks::use_location; #[function_component(Search)] fn search() -> Html { let location = use_location().unwrap(); let query_params = location.query::<SearchParams>().unwrap(); html! { <div> <h1>{ "Search Results" }</h1> <p>{ format!("Query: {}", query_params.q) }</p> <p>{ format!("Page: {}", query_params.page) }</p> </div> } } #[derive(Deserialize, Clone, PartialEq)] struct SearchParams { q: String, #[serde(default = "default_page")] page: u32, } fn default_page() -> u32 { 1 }

4. 路由守卫

rust
use yew_router::hooks::use_navigator; #[function_component(ProtectedRoute)] fn protected_route() -> Html { let is_authenticated = use_state(|| false); let navigator = use_navigator().unwrap(); // 检查认证状态 if !*is_authenticated { // 重定向到登录页面 navigator.push(&Route::Login); return html! { <div>{ "Redirecting..." }</div> }; } html! { <div> <h1>{ "Protected Content" }</h1> <p>{ "You are authenticated" }</p> </div> } }

5. 编程式导航

rust
#[function_component(NavigationExample)] fn navigation_example() -> Html { let navigator = use_navigator().unwrap(); let go_home = { let navigator = navigator.clone(); Callback::from(move |_| navigator.push(&Route::Home)) }; let go_back = { let navigator = navigator.clone(); Callback::from(move |_| navigator.back()) }; let go_forward = { let navigator = navigator.clone(); Callback::from(move |_| navigator.forward()) }; let replace_route = { let navigator = navigator.clone(); Callback::from(move |_| navigator.replace(&Route::About)) }; html! { <div> <button onclick={go_home}>{ "Go Home" }</button> <button onclick={go_back}>{ "Go Back" }</button> <button onclick={go_forward}>{ "Go Forward" }</button> <button onclick={replace_route}>{ "Replace Route" }</button> </div> } }

路由过渡动画

rust
use gloo::timers::callback::Timeout; use std::rc::Rc; use std::cell::RefCell; #[function_component(RouteTransition)] fn route_transition() -> Html { let route = use_route::<Route>().unwrap(); let is_transitioning = use_state(|| false); let current_route = use_state(|| route.clone()); // 监听路由变化 let route_effect = { let is_transitioning = is_transitioning.clone(); let current_route = current_route.clone(); use_effect(move || { if *current_route != route { is_transitioning.set(true); // 模拟过渡动画 let timeout = Timeout::new(300, move || { is_transitioning.set(false); }); timeout.forget(); } || {} }) }; let class = if *is_transitioning { "route-transitioning" } else { "route-active" }; html! { <div class={class}> <Switch /> </div> } }

路由懒加载

rust
use yew_router::Routable; use std::rc::Rc; #[derive(Clone, Routable, PartialEq)] enum Route { #[at("/")] Home, #[at("/lazy")] LazyLoaded, } // 懒加载组件 #[function_component(LazyLoaded)] fn lazy_loaded() -> Html { html! { <div> <h1>{ "Lazy Loaded Component" }</h1> <p>{ "This component was loaded on demand" }</p> </div> } } // 使用条件渲染实现懒加载 #[function_component(LazySwitch)] fn lazy_switch() -> Html { let route = use_route::<Route>().unwrap(); let lazy_component_loaded = use_state(|| false); match route { Route::LazyLoaded => { if !*lazy_component_loaded { lazy_component_loaded.set(true); } html! { <LazyLoaded /> } } _ => html! { <Switch /> }, } }

最佳实践

1. 路由组织

rust
// 将路由定义在单独的模块中 // src/routes.rs #[derive(Clone, Routable, PartialEq)] pub enum Route { #[at("/")] Home, #[at("/about")] About, // ... 其他路由 } // 将组件定义在单独的模块中 // src/components/home.rs // src/components/about.rs // ...

2. 路由守卫复用

rust
// src/guards/auth_guard.rs pub fn use_auth_guard() -> bool { let is_authenticated = use_state(|| false); let navigator = use_navigator().unwrap(); if !*is_authenticated { navigator.push(&Route::Login); false } else { true } } // 使用守卫 #[function_component(ProtectedPage)] fn protected_page() -> Html { if !use_auth_guard() { return html! { <div>{ "Redirecting..." }</div> }; } html! { <div> <h1>{ "Protected Content" }</h1> </div> } }

3. 路由错误处理

rust
#[function_component(ErrorBoundary)] fn error_boundary() -> Html { let route = use_route::<Route>().unwrap(); let error = use_state(|| None::<String>); // 错误处理逻辑 if let Some(ref err) = *error { html! { <div class="error-boundary"> <h1>{ "Something went wrong" }</h1> <p>{ err }</p> <Link<Route> to={Route::Home}>{ "Go Home" }</Link<Route>> </div> } } else { html! { <Switch /> } } }

性能优化

1. 路由预加载

rust
#[function_component(RoutePreloader)] fn route_preloader() -> Html { let navigator = use_navigator().unwrap(); let preload_route = { let navigator = navigator.clone(); Callback::from(move |_| { // 预加载路由资源 let _ = navigator.push(&Route::LazyLoaded); let _ = navigator.back(); }) }; html! { <div> <button onclick={preload_route}>{ "Preload Route" }</button> </div> } }

2. 路由缓存

rust
#[function_component(RouteCache)] fn route_cache() -> Html { let route_cache = use_mut_ref(|| std::collections::HashMap::new()); let route = use_route::<Route>().unwrap(); // 检查缓存 if let Some(cached) = route_cache.borrow().get(&route) { return html! { <div>{ cached.clone() }</div> }; } // 渲染并缓存 let content = html! { <Switch /> }; route_cache.borrow_mut().insert(route.clone(), content.clone()); content }
标签:Yew