Angular 导航
本指南介绍了路由如何在使用 Ionic 和 Angular 构建的应用中工作。
¥This guide covers how routing works in an app built with Ionic and Angular.
Angular Router 是 Angular 应用中最重要的库之一。如果没有它,应用将是单视图/单上下文应用,或者无法在浏览器重新加载时保持其导航状态。使用 Angular Router,我们可以创建可链接且具有丰富动画的丰富应用(当然与 Ionic 配合使用时)。让我们看看 Angular Router 的基础知识以及如何为 Ionic 应用配置它。
¥The Angular Router is one of the most important libraries in an Angular application. Without it, apps would be single view/single context apps or would not be able to maintain their navigation state on browser reloads. With Angular Router, we can create rich apps that are linkable and have rich animations (when paired with Ionic of course). Let's look at the basics of the Angular Router and how we can configure it for Ionic apps.
简单的路由
¥A simple Route
对于大多数应用来说,通常需要某种路由。最基本的配置看起来有点像这样:
¥For most apps, having some sort of route is often required. The most basic configuration looks a bit like this:
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
...
RouterModule.forRoot([
{ path: '', component: LoginComponent },
{ path: 'detail', component: DetailComponent },
])
],
})
我们这里最简单的细分是路径/组件查找。当我们的应用加载时,路由通过读取用户尝试加载的 URL 来启动操作。在我们的示例中,我们的路由寻找 ''
,这本质上是我们的索引路由。为此,我们加载 LoginComponent
。非常坦率的。对于路由配置中的每个条目,这种将路径与组件匹配的模式都会继续。但是如果我们想在初始加载时加载不同的路径怎么办?
¥The simplest breakdown for what we have here is a path/component lookup. When our app loads, the router kicks things off by reading the URL the user is trying to load. In our sample, our route looks for ''
, which is essentially our index route. So for this, we load the LoginComponent
. Fairly straight forward. This pattern of matching paths with a component continues for every entry we have in the router config. But what if we wanted to load a different path on our initial load?
处理重定向
¥Handling Redirects
为此,我们可以使用路由重定向。重定向的工作方式与典型的路由对象相同,但仅包含一些不同的键。
¥For this we can use router redirects. Redirects work the same way that a typical route object does, but just includes a few different keys.
[
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
{ path: 'detail', component: DetailComponent },
];
在重定向中,我们查找应用的索引路径。然后,如果我们加载它,我们就会重定向到 login
路由。pathMatch
的最后一个键用于告诉路由应如何查找路径。
¥In our redirect, we look for the index path of our app. Then if we load that, we redirect to the login
route. The last key of pathMatch
is required to tell the router how it should look up the path.
由于我们使用 full
,我们告诉路由我们应该比较完整路径,即使最终是类似 /route1/route2/route3
的路径。这意味着如果我们有:
¥Since we use full
, we're telling the router that we should compare the full path, even if ends up being something like /route1/route2/route3
. Meaning that if we have:
{ path: '/route1/route2/route3', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
并加载 /route1/route2/route3
我们将重定向。但如果我们加载 /route1/route2/route4
,我们将不会重定向,因为路径不完全匹配。
¥And load /route1/route2/route3
we'll redirect. But if we loaded /route1/route2/route4
, we won't redirect, as the paths don't match fully.
或者,如果我们使用:
¥Alternatively, if we used:
{ path: '/route1/route2', redirectTo: 'login', pathMatch: 'prefix' },
{ path: 'login', component: LoginComponent },
然后加载 /route1/route2/route3
和 /route1/route2/route4
,我们将被重定向到这两个路由。这是因为 pathMatch: 'prefix'
将仅匹配路径的一部分。
¥Then load both /route1/route2/route3
and /route1/route2/route4
, we'll be redirected for both routes. This is because pathMatch: 'prefix'
will match only part of the path.
导航到不同的路由
¥Navigating to different routes
谈论路由固然很好,但如何真正导航到所述路由呢?为此,我们可以使用 routerLink
指令。让我们回顾一下之前的简单路由设置:
¥Talking about routes is good and all, but how does one actually navigate to said routes? For this, we can use the routerLink
directive. Let's go back and take our simple router setup from earlier:
RouterModule.forRoot([
{ path: '', component: LoginComponent },
{ path: 'detail', component: DetailComponent },
]);
现在从 LoginComponent
开始,我们可以使用以下 HTML 导航到详细路由。
¥Now from the LoginComponent
, we can use the following HTML to navigate to the detail route.
<ion-header>
<ion-toolbar>
<ion-title>Login</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button [routerLink]="['/detail']">Go to detail</ion-button>
</ion-content>
这里重要的部分是 ion-button
和 routerLink
指令。RouterLink 的工作原理与典型的 href
类似,但它不是将 URL 构建为字符串,而是可以构建为数组,从而提供更复杂的路径。
¥The important part here is the ion-button
and routerLink
directive. RouterLink works on a similar idea as typical href
s, but instead of building out the URL as a string, it can be built as an array, which can provide more complicated paths.
我们还可以使用路由 API 以编程方式在我们的应用中导航。
¥We also can programmatically navigate in our app by using the router API.
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
...
})
export class LoginComponent {
constructor(private router: Router){}
navigate(){
this.router.navigate(['/detail'])
}
}
两个选项都提供相同的导航机制,只是适合不同的用例。
¥Both options provide the same navigation mechanism, just fitting different use cases.
使用 LocationStrategy.historyGo 进行导航
¥Navigating using LocationStrategy.historyGo
Angular Router 有一个 LocationStrategy.historyGo 方法,允许开发者在应用历史记录中向前或向后移动。让我们看一个例子。
¥Angular Router has a LocationStrategy.historyGo method that allows developers to move forward or backward through the application history. Let's take a look at an example.
假设你有以下应用历史记录:
¥Say you have the following application history:
/pageA
--> /pageB
--> /pageC
如果你在 /pageC
上调用 LocationStrategy.historyGo(-2)
,你将被带回到 /pageA
。如果你随后调用 LocationStrategy.historyGo(2)
,你将被带到 /pageC
。
¥If you were to call LocationStrategy.historyGo(-2)
on /pageC
, you would be brought back to /pageA
. If you then called LocationStrategy.historyGo(2)
, you would be brought to /pageC
.
LocationStrategy.historyGo()
的一个关键特性是它期望你的应用历史记录是线性的。这意味着 LocationStrategy.historyGo()
不应在使用非线性路由的应用中使用。请参阅 线性路由与非线性路由 了解更多信息。
¥An key characteristic of LocationStrategy.historyGo()
is that it expects your application history to be linear. This means that LocationStrategy.historyGo()
should not be used in applications that make use of non-linear routing. See Linear Routing versus Non-Linear Routing for more information.
延迟加载路由
¥Lazy loading routes
现在我们的路由设置的当前方式使得它们包含在与根 app.module 相同的块中,这并不理想。相反,路由有一个设置,允许将组件隔离到它们自己的块中。
¥Now the current way our routes are setup makes it so they are included in the same chunk as the root app.module, which is not ideal. Instead, the router has a setup that allows the components to be isolated to their own chunks.
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
...
RouterModule.forRoot([
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', loadChildren: () => import('./login/login.module').then(m => m.LoginModule) },
{ path: 'detail', loadChildren: () => import('./detail/detail.module').then(m => m.DetailModule) }
])
],
})
虽然类似,但 loadChildren
属性是一种通过使用原生导入而不是直接使用组件来引用模块的方法。为了做到这一点,我们需要为每个组件创建一个模块。
¥While similar, the loadChildren
property is a way to reference a module by using native import instead of a component directly. In order to do this though, we need to create a module for each of the components.
...
import { RouterModule } from '@angular/router';
import { LoginComponent } from './login.component';
@NgModule({
imports: [
...
RouterModule.forChild([
{ path: '', component: LoginComponent },
])
],
})
我们排除了一些额外的内容,只包含必要的部分。
¥We're excluding some additional content and only including the necessary parts.
在这里,我们有一个典型的 Angular 模块设置,以及 RouterModule 导入,但我们现在使用 forChild
并在该设置中声明该组件。通过此设置,当我们运行构建时,我们将为应用组件、登录组件和详细信息组件生成单独的块。
¥Here, we have a typical Angular Module setup, along with a RouterModule import, but we're now using forChild
and declaring the component in that setup. With this setup, when we run our build, we will produce separate chunks for both the app component, the login component, and the detail component.
独立组件
¥Standalone Components
独立组件允许开发者在路由上延迟加载组件,而无需向 Angular 模块声明该组件。
¥Standalone components allow developers to lazy load a component on a route without having to declare the component to an Angular module.
开发者可以使用现有语法从 Angular 进行独立组件路由:
¥Developers can use the existing syntax for standalone component routing from Angular:
@NgModule({
imports: [
RouterModule.forRoot([
{
path: 'standalone-route',
loadComponent: () => import('./path/to/my-component.component').then((c) => c.MyComponent),
},
]),
],
})
export class AppRoutingModule {}
如果你使用 routerLink
、routerDirection
或 routerAction
,请确保还导入 Ionic 组件的 IonRouterLink
指令或 <a>
元素的 IonRouterLinkWithHref
指令。Ionic Angular 构建选项文档 中提供了一个示例。
¥If you are using routerLink
, routerDirection
, or routerAction
be sure to also import the IonRouterLink
directive for Ionic components or the IonRouterLinkWithHref
directive for <a>
elements. An example of this is available in the Ionic Angular Build Options docs.
开始使用独立组件 访问 Angular 的官方文档。
¥To get started with standalone components visit Angular's official docs.
实例
¥Live Example
如果你希望亲主体验上述概念和代码,请查看 StackBlitz 上的上述主题的 活生生的例子。
¥If you would prefer to get hands on with the concepts and code described above, please checkout our live example of the topics above on StackBlitz.
线性路由与非线性路由
¥Linear Routing versus Non-Linear Routing
线性路由
¥Linear Routing
如果你构建了一个使用路由的 Web 应用,那么你以前可能使用过线性路由。线性路由意味着你可以通过推送和弹出页面在应用历史记录中向前或向后移动。
¥If you have built a web app that uses routing, you likely have used linear routing before. Linear routing means that you can move forward or backward through the application history by pushing and popping pages.
以下是移动应用中线性路由的示例:
¥The following is an example of linear routing in a mobile app:
本例中的应用历史记录具有以下路径:
¥The application history in this example has the following path:
Accessibility
--> VoiceOver
--> Speech
当我们按下后退按钮时,我们会遵循相同的路由路径,但方向相反。线性路由很有用,因为它允许简单且可预测的路由行为。这也意味着我们可以使用路由 Angular Router API,例如 LocationStrategy.historyGo()。
¥When we press the back button, we follow that same routing path except in reverse. Linear routing is helpful in that it allows for simple and predictable routing behaviors. It also means we can use router Angular Router APIs such as LocationStrategy.historyGo().
线性路由的缺点是它不允许复杂的用户体验,例如选项卡视图。这就是非线性路由发挥作用的地方。
¥The downside of linear routing is that it does not allow for complex user experiences such as tab views. This is where non-linear routing comes into play.
非线性路由
¥Non-Linear Routing
对于许多学习使用 Ionic 构建移动应用的 Web 开发者来说,非线性路由可能是一个新概念。
¥Non-linear routing is a concept that may be new to many web developers learning to build mobile apps with Ionic.
非线性路由意味着用户应该返回的视图不一定是屏幕上显示的前一个视图。
¥Non-linear routing means that the view that the user should go back to is not necessarily the previous view that was displayed on the screen.
以下是非线性路由的示例:
¥The following is an example of non-linear routing:
在上面的示例中,我们从 Originals
选项卡开始。点击卡片即可进入 Originals
选项卡中的 Ted Lasso
视图。
¥In the example above, we start on the Originals
tab. Tapping a card brings us to the Ted Lasso
view within the Originals
tab.
从这里,我们切换到 Search
选项卡。然后,我们再次点击 Originals
选项卡,返回到 Ted Lasso
视图。至此,我们已经开始使用非线性路由。
¥From here, we switch to the Search
tab. Then, we tap the Originals
tab again and are brought back to the Ted Lasso
view. At this point, we have started using non-linear routing.
为什么这是非线性路由?我们之前看到的视图是 Search
视图。然而,按 Ted Lasso
视图上的后退按钮应该会让我们回到根 Originals
视图。发生这种情 况是因为移动应用中的每个选项卡都被视为自己的堆栈。使用选项卡 部分对此进行了更详细的讨论。
¥Why is this non-linear routing? The previous view we were on was the Search
view. However, pressing the back button on the Ted Lasso
view should bring us back to the root Originals
view. This happens because each tab in a mobile app is treated as its own stack. The Working with Tabs sections goes over this in more detail.
如果从 Ted Lasso
视图中点击简单地称为 LocationStrategy.historyGo(-1)
的后退按钮,我们将返回到 Search
视图,这是不正确的。
¥If tapping the back button simply called LocationStrategy.historyGo(-1)
from the Ted Lasso
view, we would be brought back to the Search
view which is not correct.
非线性路由允许线性路由无法处理的复杂用户流。然而,某些线性路由 API(例如 LocationStrategy.historyGo()
)不能在这种非线性环境中使用。这意味着在使用选项卡或嵌套插座时不应使用 LocationStrategy.historyGo()
。
¥Non-linear routing allows for sophisticated user flows that linear routing cannot handle. However, certain linear routing APIs such as LocationStrategy.historyGo()
cannot be used in this non-linear environment. This means that LocationStrategy.historyGo()
should not be used when using tabs or nested outlets.
我应该选择哪一个?
¥Which one should I choose?
我们建议你的应用尽可能简单,直到你需要添加非线性路由。 非线性路由非常强大,但它也给移动应用增加了相当多的复杂性。
¥We recommend keeping your application as simple as possible until you need to add non-linear routing. Non-linear routing is very powerful, but it also adds a considerable amount of complexity to mobile applications.
非线性布线的两个最常见用途是使用制表符和嵌套 ion-router-outlet
。如果你的应用满足选项卡或嵌套路由出口用例,我们建议仅使用非线性路由。
¥The two most common uses of non-linear routing is with tabs and nested ion-router-outlet
s. We recommend only using non-linear routing if your application meets the tabs or nested router outlet use cases.
有关选项卡的更多信息,请参阅 使用选项卡。
¥For more on tabs, please see Working with Tabs.
有关嵌套路由插座的更多信息,请参阅 嵌套路由。
¥For more on nested router outlets, please see Nested Routes.
共享 URL 与嵌套路由
¥Shared URLs versus Nested Routes
设置路由时一个常见的混淆点是在共享 URL 或嵌套路由之间做出选择。本指南的这一部分将解释两者并帮助你决定使用哪一个。
¥A common point of confusion when setting up routing is deciding between shared URLs or nested routes. This part of the guide will explain both and help you decide which one to use.