<A/>组件
客户端导航与普通HTML <a>元素配合得非常好。路由器添加了一个监听器,处理对<a>元素的每次点击,并尝试在客户端处理它,即,不需要再次往返服务器请求HTML。这就是您可能从大多数现代Web应用程序中熟悉的快速"单页应用程序"导航的原因。
路由器在以下情况下会放弃处理<a>点击
- 点击事件已调用
prevent_default() - 点击时按住了Meta、Alt、Ctrl或Shift键
<a>有target或download属性,或rel="external"- 链接与当前位置有不同的来源
换句话说,路由器只有在相当确定可以处理时才会尝试进行客户端导航,它会升级每个<a>元素以获得这种特殊行为。
这也意味着如果您需要选择退出客户端路由,您可以轻松做到。例如,如果您有一个指向同一域上另一个页面的链接,但它不是您的Leptos应用程序的一部分,您可以只使用
<a rel="external">来告诉路由器这不是它可以处理的东西。
路由器还提供了一个<A>组件,它做了两件额外的事情:
- 正确解析相对嵌套路由。使用普通
<a>标签的相对路由可能很棘手。例如,如果您有一个像/post/:id这样的路由,<A href="1">将生成正确的相对路由,但<a href="1"可能不会(取决于它在您的视图中出现的位置)。<A/>相对于它出现的嵌套路由的路径解析路由。 - 如果此链接是活动链接(即,它是指向您所在页面的链接),则将
aria-current属性设置为page。这对可访问性和样式很有帮助。例如,如果您想在链接指向您当前所在页面时设置不同的颜色,您可以使用CSS选择器匹配此属性。
程序化导航
您在页面之间导航的最常用方法应该是使用<a>和<form>元素或增强的<A/>和<Form/>组件。使用链接和表单进行导航是可访问性和优雅降级的最佳解决方案。
不过,有时您会想要程序化导航,即调用可以导航到新页面的函数。在这种情况下,您应该使用use_navigate函数。
let navigate = leptos_router::hooks::use_navigate();
navigate("/somewhere", Default::default());
您几乎永远不应该做像
<button on:click=move |_| navigate(/* ... */)>这样的事情。任何导航的on:click都应该是<a>,出于可访问性的原因。
这里的第二个参数是一组NavigateOptions,包括相对于当前路由解析导航的选项(如<A/>组件所做的),在导航堆栈中替换它,包含一些导航状态,以及在导航时维护当前滚动状态。
再次,这是相同的示例。查看相对
<A/>组件,并查看index.html中的CSS以查看基于ARIA的样式。
CodeSandbox Source
use leptos::prelude::*;
use leptos_router::components::{Outlet, ParentRoute, Route, Router, Routes, A};
use leptos_router::hooks::use_params_map;
use leptos_router::path;
#[component]
pub fn App() -> impl IntoView {
view! {
<Router>
<h1>"Contact App"</h1>
// 这个<nav>将在每个路由上显示,
// 因为它在<Routes/>之外
// 注意:我们可以只使用普通的<a>标签
// 路由器将使用客户端导航
<nav>
<a href="/">"Home"</a>
<a href="/contacts">"Contacts"</a>
</nav>
<main>
<Routes fallback=|| "Not found.">
// / 只有一个非嵌套的"Home"
<Route path=path!("/") view=|| view! {
<h3>"Home"</h3>
}/>
// /contacts 有嵌套路由
<ParentRoute
path=path!("/contacts")
view=ContactList
>
// 如果没有指定id,回退
<ParentRoute path=path!(":id") view=ContactInfo>
<Route path=path!("") view=|| view! {
<div class="tab">
"(Contact Info)"
</div>
}/>
<Route path=path!("conversations") view=|| view! {
<div class="tab">
"(Conversations)"
</div>
}/>
</ParentRoute>
// 如果没有指定id,回退
<Route path=path!("") view=|| view! {
<div class="select-user">
"Select a user to view contact info."
</div>
}/>
</ParentRoute>
</Routes>
</main>
</Router>
}
}
#[component]
fn ContactList() -> impl IntoView {
view! {
<div class="contact-list">
// 这里是我们的联系人列表组件本身
<h3>"Contacts"</h3>
<div class="contact-list-contacts">
<A href="alice">"Alice"</A>
<A href="bob">"Bob"</A>
<A href="steve">"Steve"</A>
</div>
// <Outlet/>将显示嵌套的子路由
// 我们可以在布局中的任何地方定位这个outlet
<Outlet/>
</div>
}
}
#[component]
fn ContactInfo() -> impl IntoView {
// 我们可以使用`use_params_map`响应式地访问:id参数
let params = use_params_map();
let id = move || params.read().get("id").unwrap_or_default();
// 想象我们在这里从API加载数据
let name = move || match id().as_str() {
"alice" => "Alice",
"bob" => "Bob",
"steve" => "Steve",
_ => "User not found.",
};
view! {
<h4>{name}</h4>
<div class="contact-info">
<div class="tabs">
<A href="" exact=true>"Contact Info"</A>
<A href="conversations">"Conversations"</A>
</div>
// 这里的<Outlet/>是嵌套在
// /contacts/:id路由下的选项卡
<Outlet/>
</div>
}
}
fn main() {
leptos::mount::mount_to_body(App)
}