Angular 导航
本指南介绍在使用 Ionic 和 Angular 构建的应用中路由是如何工作的。
Angular 路由器是 Angular 应用程序中最重要的库之一。没有它,应用程序将只能呈现单一视图或单一上下文,或者在浏览器刷新时无法维护其导航状态。借助 Angular 路由器,我们可以创建内容丰富、可链接且具有丰富动画(当然,当与 Ionic 配对时)的应用。让我们看看 Angular 路由器的基础知识以及我们如何为 Ionic 应用配置它。
一个简单的路由
对于大多数应用来说,某种形式的路由通常是必需的。最基本的配置看起来像这样:
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
...
RouterModule.forRoot([
{ path: '', component: LoginComponent },
{ path: 'detail', component: DetailComponent },
])
],
})
这里最简单的分解是路径/组件的查找。当我们的应用加载时,路由器通过读取用户试图加载的 URL 来启动。在我们的示例中,我们的路由查找 '',这本质上就是我们的索引路由。因此,我们会加载 LoginComponent。相当直接。这种将路径与组件匹配的模式对我们路由器配置中的每个条目都适用。但是,如果我们想在初始加载时加载一个不同的路径呢?
处理重定向
为此,我们可以使用路由器重定向。重定向的工作方式与典型的路由对象相同,只是包含了一些不同的键。
[
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
{ path: 'detail', component: DetailComponent },
];
在我们的重定向中,我们查找应用的索引路径。然后,如果加载该路径,我们会重定向到 login 路由。最后的 pathMatch 键是必需的,用来告诉路由器应该如何查找路径。
由于我们使用了 full,我们告诉路由器应该比较完整的路径,即使它最终是像 /route1/route2/route3 这样的路径。这意味着,如果我们有:
{ path: '/route1/route2/route3', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', component: LoginComponent },
然后加载 /route1/route2/route3,我们会重定向。但是如果我们加载 /route1/route2/route4,我们不会重定向,因为路径不完全匹配。
或者,如果我们使用:
{ path: '/route1/route2', redirectTo: 'login', pathMatch: 'prefix' },
{ path: 'login', component: LoginComponent },
那么加载 /route1/route2/route3 和 /route1/route2/route4 时,我们都会被重定向到这两个路由。这是因为 pathMatch: 'prefix' 只会匹配路径的一部分。
导航到不同的路由
讨论路由本身是好的,但是如何实际导航到这些路由呢?为此,我们可以使 用 routerLink 指令。让我们回顾一下我们之前的简单路由器设置:
RouterModule.forRoot([
{ path: '', component: LoginComponent },
{ path: 'detail', component: DetailComponent },
]);
现在,在 LoginComponent 中,我们可以使用以下 HTML 导航到详情路由。
<ion-header>
<ion-toolbar>
<ion-title>登录</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button [routerLink]="['/detail']">前往详情</ion-button>
</ion-content>
这里的关键部分是 ion-button 和 routerLink 指令。RouterLink 的工作方式类似于典型的 href,但不是将 URL 构建为字符串,而是可以构建为数组,这可以提供更复杂的路径。
我们还可以通过使用路由器 API 在应用程序中以编程方式进行导航。
import { Component } from '@angular/core';
import { Router } from '@angular/router';
@Component({
...
})
export class LoginComponent {
constructor(private router: Router){}
navigate(){
this.router.navigate(['/detail'])
}
}
两种选项都提供相同的导航机制,只是适用于不同的使用场景。
使用 LocationStrategy.historyGo 导航
Angular 路由器有一个 LocationStrategy.historyGo 方法,允许开发者在应用历史记录中向前或向后移动。让我们看一个例子。
假设你有以下应用历史记录:
/pageA --> /pageB --> /pageC
如果你在 /pageC 调用 LocationStrategy.historyGo(-2),你会被带回到 /pageA。如果你随后调用 LocationStrategy.historyGo(2),你会被带到 /pageC。
LocationStrategy.historyGo() 的一个关键特性是它期望你的应用历史记录是线性的。这意味着 LocationStrategy.historyGo() 不应该在使用非线性路由的应用中使用。更多信息请参见线性路由与非线性路由。
惰性加载路由
目前我们设置路由的方式是让它们与根 app.module 包含在同一个代码块中,这并不理想。相反,路由器有一种设置允许将组件隔离到它们自己的代码块中。
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 属性是一种通过使用原生 import 而不是直接引用组件来引用模块的方式。但是,要这样做,我们需要为每个组件创建一个模块。
...
import { RouterModule } from '@angular/router';
import { LoginComponent } from './login.component';
@NgModule({
imports: [
...
RouterModule.forChild([
{ path: '', component: LoginComponent },
])
],
})
我们省略了一些额外内容,只包含必要的部分。
这里,我们有一个典型的 Angular 模块设置,以及一个 RouterModule 导入,但是我们现在使用了 forChild 并在该设置中声明了组件。通过这种设置,当我们运行构建时,我们将为应用组件、登录组件和详情组件生成单独的代码块。
独立组件
独立组件是 Angular 14.x 中引入的一个实验性 API,在 Ionic 6.3 及更高版本中可用。此功能在稳定之前可能会发生变化。
独立组件允许开发者在一个路由上惰性加载一个组件,而无需将该组件声明到 Angular 模块中。
要在路由和 Ionic 框架中使用独立组件,你必须首先使用 Ionic ^6.3.0。这个实验性 API 要求开发者为每个使用独立组件路由的路由出口(ion-router-outlet 和 ion-tabs)分配 EnvironmentInjector 实例。
import { Component, EnvironmentInjector } from '@angular/core';
@Component({
selector: 'app-root',
template: 'app.component.html',
})
export class AppComponent {
constructor(public environmentInjector: EnvironmentInjector) {}
}
<ion-router-outlet [environmentInjector]="environmentInjector"></ion-router-outlet>
<!-- 或者如果你正在使用 ion-tabs -->
<ion-tabs [environmentInjector]="environmentInjector"> ... </ion-tabs>
开发者可以使用 Angular 中现有的独立组件路由语法:
@NgModule({
imports: [
RouterModule.forRoot([
{
path: 'standalone-route',
loadComponent: () => import('./path/to/my-component.component').then((c) => c.MyComponent),
},
]),
],
})
export class AppRoutingModule {}
要开始使用独立组件,请访问 Angular 官方文档。
在线示例
如果你想亲身体验上述概念和代码,请在 StackBlitz 上查看我们关于上述主题的在线示例。