<A/> 组件
The <A/> Component
使用普通的 HTML <a> 元素,客户端导航也能完美运行。路由管理器添加了一个监听器,用于处理对 <a> 元素的每一次点击,并尝试在客户端处理它,即无需再次往返服务器请求 HTML。这就是让你在大多数现代 Web 应用中倍感亲切的、迅速的“单页应用”导航。
Client-side navigation works perfectly fine with ordinary HTML <a> elements. The router adds a listener that handles every click on a <a> element and tries to handle it on the client side, i.e., without doing another round trip to the server to request HTML. This is what enables the snappy “single-page app” navigations you’re probably familiar with from most modern web apps.
在以下几种情况下,路由管理器会放弃处理 <a> 点击:
The router will bail out of handling an <a> click under a number of situations
-
点击事件已调用
prevent_default() -
the click event has had
prevent_default()called on it -
点击时按住了 Meta、Alt、Ctrl 或 Shift 键
-
the Meta, Alt, Ctrl, or Shift keys were held during click
-
<a>具有target或download属性,或者rel="external" -
the
<a>has atargetordownloadattribute, orrel="external" -
链接的源(origin)与当前位置不同
-
the link has a different origin from the current location
换句话说,路由管理器只有在非常确定能够处理时才会尝试进行客户端导航,并且它会升级每一个 <a> 元素以获得这种特殊行为。
In other words, the router will only try to do a client-side navigation when it’s pretty sure it can handle it, and it will upgrade every <a> element to get this special behavior.
这也意味着如果你需要退出客户端路由,可以很容易地做到。例如,如果你有一个指向同一域名下但不属于 Leptos 应用的其他页面的链接,你只需使用
<a rel="external">来告诉路由管理器这不是它能处理的东西。
This also means that if you need to opt out of client-side routing, you can do so easily. For example, if you have a link to another page on the same domain, but which isn’t part of your Leptos app, you can just use
<a rel="external">to tell the router it isn’t something it can handle.
路由管理器还提供了一个 <A> 组件,它额外做了两件事:
The router also provides an <A> component, which does two additional things:
-
正确解析相对嵌套路由。使用普通的
<a>标签进行相对路由可能会很棘手。例如,如果你有一个像/post/:id这样的路由,<A href="1">会生成正确的相对路由,但<a href="1">很可能不会(取决于它在视图中出现的位置)。<A/>会相对于它所在的嵌套路由路径来解析路由。 -
Correctly resolves relative nested routes. Relative routing with ordinary
<a>tags can be tricky. For example, if you have a route like/post/:id,<A href="1">will generate the correct relative route, but<a href="1">likely will not (depending on where it appears in your view.)<A/>resolves routes relative to the path of the nested route within which it appears. -
如果此链接是当前活动链接(即指向你当前所在页面的链接),则将
aria-current属性设置为page。这对于可访问性和样式设置非常有帮助。例如,如果你想在链接指向当前页面时将其设置为不同的颜色,你可以使用 CSS 选择器匹配此属性。 -
Sets the
aria-currentattribute topageif this link is the active link (i.e., it’s a link to the page you’re on). This is helpful for accessibility and for styling. For example, if you want to set the link a different color if it’s a link to the page you’re currently on, you can match this attribute with a CSS selector.
以编程方式导航
Navigating Programmatically
在页面之间导航最常用的方法应该是使用 <a> 和 <form> 元素,或者使用增强的 <A/> 和 <Form/> 组件。使用链接和表单进行导航是实现可访问性和平稳退化的最佳方案。
Your most-used methods of navigating between pages should be with <a> and <form> elements or with the enhanced <A/> and <Form/> components. Using links and forms to navigate is the best solution for accessibility and graceful degradation.
不过,有时你可能想以编程方式导航,即调用一个可以导航到新页面的函数。在这种情况下,你应该使用 use_navigate 函数。
On occasion, though, you’ll want to navigate programmatically, i.e., call a function that can navigate to a new page. In that case, you should use the use_navigate function.
let navigate = leptos_router::hooks::use_navigate();
navigate("/somewhere", Default::default());
你几乎永远不应该做类似
<button on:click=move |_| navigate(/* ... */)>这样的事情。出于可访问性的原因,任何触发导航的on:click都应该是一个<a>。
You should almost never do something like
<button on:click=move |_| navigate(/* ... */)>. Anyon:clickthat navigates should be an<a>, for reasons of accessibility.
这里的第二个参数是一组 NavigateOptions,其中包括像 <A/> 组件那样相对于当前路由解析导航、在导航栈中替换当前记录、包含一些导航状态以及在导航时保持当前滚动状态等选项。
The second argument here is a set of NavigateOptions, which includes options to resolve the navigation relative to the current route as the <A/> component does, replace it in the navigation stack, include some navigation state, and maintain the current scroll state on navigation.
再次强调,这是同一个示例。请查看相对路径的
<A/>组件,并查看index.html中的 CSS 以了解基于 ARIA 的样式设置。
Once again, this is the same example. Check out the relative
<A/>components, and take a look at the CSS inindex.htmlto see the ARIA-based styling.
:::admonish sandbox title="在线示例" collapsible=true
:::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/> 之外
// this <nav> will show on every route,
// because it's outside the <Routes/>
// 注意:我们可以直接使用普通的 <a> 标签
// 路由管理器将使用客户端导航
// note: we can just use normal <a> tags
// and the router will use client-side navigation
<nav>
<a href="/">"Home"</a>
<a href="/contacts">"Contacts"</a>
</nav>
<main>
<Routes fallback=|| "Not found.">
// / 只有一个未嵌套的 "Home"
// / just has an un-nested "Home"
<Route path=path!("/") view=|| view! {
<h3>"Home"</h3>
}/>
// /contacts 有嵌套路由
// /contacts has nested routes
<ParentRoute
path=path!("/contacts")
view=ContactList
>
// 如果未指定 id,则回退
// if no id specified, fall back
<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,则回退
// if no id specified, fall back
<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">
// 这里是我们联系人列表组件本身
// here's our contact list component itself
<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/> will show the nested child route
// we can position this outlet wherever we want
// within the layout
<Outlet/>
</div>
}
}
#[component]
fn ContactInfo() -> impl IntoView {
// 我们可以使用 `use_params_map` 响应式地访问 :id 参数
// we can access the :id param reactively with `use_params_map`
let params = use_params_map();
let id = move || params.read().get("id").unwrap_or_default();
// 假设我们在这里从 API 加载数据
// imagine we're loading data from an API here
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/> here is the tabs that are nested
// underneath the /contacts/:id route
<Outlet/>
</div>
}
}
fn main() {
leptos::mount::mount_to_body(App)
}