<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::use_navigate();
navigate("/somewhere", Default::default());
你几乎不应该做类似
<button on:click=move |_| navigate(/* ... */)>
的事情。出于可访问性的原因,任何导航的on:click
都应该是<a>
。
这里的第二个参数是一组 NavigateOptions
,其中包括相对于当前路由解析导航的选项(如 <A/>
组件所做的那样),在导航堆栈中替换它,包含一些导航状态,并在导航时维护当前滚动状态。
再一次,这与之前的例子相同。查看相关的
<A/>
组件,并查看index.html
中的 CSS,以了解基于 ARIA 的样式。
实时示例
CodeSandbox 源码
use leptos::*;
use leptos_router::*;
#[component]
fn App() -> impl IntoView {
view! {
<Router>
<h1>"Contact App"</h1>
// 这个 <nav> 将显示在每个路由上,
// 因为它在 <Routes/> 之外
// 注意:我们可以只使用普通的 <a> 标签
// 路由器将使用客户端导航
<nav>
<h2>"Navigation"</h2>
<a href="/">"Home"</a>
<a href="/contacts">"Contacts"</a>
</nav>
<main>
<Routes>
// / 只有一个未嵌套的 "Home"
<Route path="/" view=|| view! {
<h3>"Home"</h3>
}/>
// /contacts 有嵌套路由
<Route
path="/contacts"
view=ContactList
>
// 如果没有指定 id,则回退
<Route path=":id" view=ContactInfo>
<Route path="" view=|| view! {
<div class="tab">
"(Contact Info)"
</div>
}/>
<Route path="conversations" view=|| view! {
<div class="tab">
"(Conversations)"
</div>
}/>
</Route>
// 如果没有指定 id,则回退
<Route path="" view=|| view! {
<div class="select-user">
"Select a user to view contact info."
</div>
}/>
</Route>
</Routes>
</main>
</Router>
}
}
#[component]
fn ContactList() -> impl IntoView {
view! {
<div class="contact-list">
// 这是我们的联系人列表组件本身
<div class="contact-list-contacts">
<h3>"Contacts"</h3>
<A href="alice">"Alice"</A>
<A href="bob">"Bob"</A>
<A href="steve">"Steve"</A>
</div>
// <Outlet/> 将显示嵌套的子路由
// 我们可以将此出口放置在布局中的任何位置
<Outlet/>
</div>
}
}
#[component]
fn ContactInfo() -> impl IntoView {
// 我们可以使用 `use_params_map` 以响应式方式访问 :id 参数
let params = use_params_map();
let id = move || params.with(|params| params.get("id").cloned().unwrap_or_default());
// 假设我们在这里从 API 加载数据
let name = move || match id().as_str() {
"alice" => "Alice",
"bob" => "Bob",
"steve" => "Steve",
_ => "User not found.",
};
view! {
<div class="contact-info">
<h4>{name}</h4>
<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_to_body(App)
}