构建一个Nest
在本章节中,我们将开始构建我们网站的 blog 部分,这将包括链接、嵌套路由和路由参数。
网站导航
我们的网站访问者不会知道我们网站上所有可用的页面和博客,因此我们应该为他们提供一个导航栏。我们的导航栏将是一系列链接,用于在我们页面之间跳转。
我们希望我们的导航栏组件在网站上的多个不同页面上渲染。与其复制代码,我们可以创建一个包装所有子路由的组件。这被称为布局组件。要告诉路由器在哪里渲染子路由,我们使用 Outlet 组件。
我们来创建一个新的 NavBar 组件:
#[component]
fn NavBar() -> Element {
rsx! {
nav {
ul { li { "links" } }
}
// `Outlet` 组件将在 `Outlet` 组件内部渲染子路由(在这种情况下,只是 `Home` 组件)
Outlet::<Route> {}
}
}
接下来,让我们将我们的 NavBar 组件作为布局添加到我们的 Route 枚举中:
#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
// 在 `NavBar` 布局下的所有路由将在 `NavBar` 的 `Outlet` 中渲染
#[layout(NavBar)]
#[route("/")]
Home {},
#[end_layout]
#[route("/:..route")]
PageNotFound { route: Vec<String> },
}
要在我们的 NavBar 中添加链接,我们总是可以使用 HTML 锚元素,但这有两个问题:
- 它会导致全页面刷新
- 我们可能会不小心链接到不存在的页面
相反,我们想使用 Dioxus 路由器提供的 Link 组件。
Link 与常规 <a> 标签类似。它接受一个目标和子元素。
与常规 <a> 标签不同,我们可以将我们的 Route 枚举作为目标传递。因为我们用 #[route(path)] 属性注解了我们的路由,Link 将知道如何生成正确的 URL。如果我们使用 Route 枚举,Rust 编译器将防止我们链接到不存在的页面。
我们来添加我们的链接:
#[component]
fn NavBar() -> Element {
rsx! {
nav {
ul {
li {
Link { to: Route::Home {}, "Home" }
}
}
}
Outlet::<Route> {}
}
}
使用这种方法,
Link组件仅适用于我们应用程序内的链接。要了解更多关于导航目标的信息,请参见此处。
现在你应该在页面顶部看到一个链接列表。点击其中一个,你应该可以无缝地在页面之间跳转。
URL 参数和嵌套路由
许多网站,如 GitHub,将参数放在他们的 URL 中。例如,https://github.com/DioxusLabs 利用域名后的文本动态搜索并显示有关组织的相关内容。
我们希望将我们的博客存储在数据库中,并按需加载。我们也希望我们的用户能够发送链接给特定的博客文章。与其在编译时列出所有博客标题,我们可以制作一个动态路由。
我们的 blog 路径将看起来像 /blog/myBlogPage,myBlogPage 是 URL 参数。
首先,我们来创建一个布局组件(类似于导航栏),它包装 blog 内容。这允许我们添加一个标题,告诉用户他们正在阅读博客。
#[component]
fn Blog() -> Element {
rsx! {
h1 { "Blog" }
Outlet::<Route> {}
}
}
现在我们将创建另一个索引组件,当没有选择博客文章时将显示:
#[component]
fn BlogList() -> Element {
rsx! {
h2 { "Choose a post" }
ul {
li {
Link {
to: Route::BlogPost {
name: "Blog post 1".into(),
},
"Read the first blog post"
}
}
li {
Link {
to: Route::BlogPost {
name: "Blog post 2".into(),
},
"Read the second blog post"
}
}
}
}
}
我们还需要创建一个显示实际博客文章的组件。这个组件将接受 URL 参数作为属性:
// name 属性来自 /:name 路由段
#[component]
fn BlogPost(name: String) -> Element {
rsx! { h2 { "Blog Post: {name}" } }
}
最后,让我们告诉路由器这些组件:
#[derive(Routable, Clone)]
#[rustfmt::skip]
enum Route {
#[layout(NavBar)]
#[route("/")]
Home {},
#[nest("/blog")]
#[layout(Blog)]
#[route("/")]
BlogList {},
#[route("/post/:name")]
BlogPost { name: String },
#[end_layout]
#[end_nest]
#[end_layout]
#[route("/:..route")]
PageNotFound {
route: Vec<String>,
},
}
就是这样!如果你访问 /blog/1,你应该能看到我们的示例文章。
结论
在本章节中,我们利用了 Dioxus 路由器的 Link 和路由参数功能来构建了我们应用程序的 blog 部分。在下一章中,我们将讨论导航目标(比如我们传递给链接的那个)的工作原理。