Ionic高级导航 #
一、路由守卫 #
1.1 CanActivate守卫 #
typescript
// guards/auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AuthService } from '../services/auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private auth: AuthService,
private router: Router
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
if (this.auth.isLoggedIn()) {
return true;
}
// 保存目标URL
this.auth.setRedirectUrl(state.url);
// 跳转到登录页
this.router.navigate(['/auth/login']);
return false;
}
}
// 路由配置
const routes: Routes = [
{
path: 'profile',
loadChildren: () => import('./profile/profile.module').then(m => m.ProfilePageModule),
canActivate: [AuthGuard]
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsPageModule),
canActivate: [AuthGuard]
}
];
1.2 CanActivateChild守卫 #
typescript
// guards/admin.guard.ts
import { Injectable } from '@angular/core';
import { CanActivateChild, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
@Injectable({
providedIn: 'root'
})
export class AdminGuard implements CanActivateChild {
constructor(
private auth: AuthService,
private router: Router
) {}
canActivateChild(): boolean {
if (this.auth.isAdmin()) {
return true;
}
this.router.navigate(['/forbidden']);
return false;
}
}
// 路由配置
const routes: Routes = [
{
path: 'admin',
canActivateChild: [AdminGuard],
children: [
{ path: 'users', component: UsersPage },
{ path: 'settings', component: AdminSettingsPage }
]
}
];
1.3 CanDeactivate守卫 #
typescript
// guards/unsaved-changes.guard.ts
import { Injectable } from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { AlertController } from '@ionic/angular';
export interface CanDeactivateComponent {
canDeactivate: () => boolean | Promise<boolean>;
}
@Injectable({
providedIn: 'root'
})
export class UnsavedChangesGuard implements CanDeactivate<CanDeactivateComponent> {
constructor(private alertCtrl: AlertController) {}
async canDeactivate(
component: CanDeactivateComponent
): Promise<boolean> {
if (!component.canDeactivate || component.canDeactivate()) {
return true;
}
const alert = await this.alertCtrl.create({
header: '离开确认',
message: '您有未保存的更改,确定要离开吗?',
buttons: [
{
text: '取消',
role: 'cancel',
handler: () => false
},
{
text: '确定离开',
handler: () => true
}
]
});
await alert.present();
const { role } = await alert.onDidDismiss();
return role !== 'cancel';
}
}
// 组件实现
@Component({
selector: 'app-edit',
templateUrl: 'edit.page.html'
})
export class EditPage implements CanDeactivateComponent {
hasUnsavedChanges = false;
canDeactivate(): boolean {
return !this.hasUnsavedChanges;
}
}
// 路由配置
const routes: Routes = [
{
path: 'edit/:id',
component: EditPage,
canDeactivate: [UnsavedChangesGuard]
}
];
1.4 Resolve守卫 #
typescript
// resolvers/user.resolver.ts
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { UserService } from '../services/user.service';
@Injectable({
providedIn: 'root'
})
export class UserResolver implements Resolve<any> {
constructor(
private userService: UserService,
private router: Router
) {}
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> {
const id = route.paramMap.get('id');
return this.userService.getUser(id).pipe(
catchError(error => {
this.router.navigate(['/not-found']);
return of(null);
})
);
}
}
// 路由配置
const routes: Routes = [
{
path: 'user/:id',
component: UserPage,
resolve: {
user: UserResolver
}
}
];
// 组件获取数据
@Component({
selector: 'app-user',
templateUrl: 'user.page.html'
})
export class UserPage implements OnInit {
user: any;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.user = this.route.snapshot.data['user'];
}
}
二、懒加载 #
2.1 模块懒加载 #
typescript
// app-routing.module.ts
const routes: Routes = [
{
path: 'home',
loadChildren: () => import('./home/home.module').then(m => m.HomePageModule)
},
{
path: 'detail/:id',
loadChildren: () => import('./detail/detail.module').then(m => m.DetailPageModule)
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsPageModule)
}
];
2.2 预加载策略 #
typescript
// 预加载所有模块
import { PreloadAllModules } from '@angular/router';
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules
})
]
})
export class AppRoutingModule {}
// 自定义预加载策略
import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
// 只预加载带有preload标志的路由
if (route.data && route.data['preload']) {
return load();
}
return of(null);
}
}
// 路由配置
const routes: Routes = [
{
path: 'home',
loadChildren: () => import('./home/home.module').then(m => m.HomePageModule),
data: { preload: true }
},
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsPageModule)
// 不预加载
}
];
2.3 懒加载模块结构 #
text
src/app/
├── home/
│ ├── home.module.ts
│ ├── home.page.ts
│ ├── home.page.html
│ ├── home.page.scss
│ └── home-routing.module.ts
├── detail/
│ ├── detail.module.ts
│ ├── detail.page.ts
│ ├── detail.page.html
│ ├── detail.page.scss
│ └── detail-routing.module.ts
└── app-routing.module.ts
三、导航动画 #
3.1 自定义页面过渡 #
typescript
import { AnimationController } from '@ionic/angular';
// 自定义进入动画
const enterAnimation = (baseEl: HTMLElement, opts?: any) => {
const animationCtrl = new AnimationController();
const backdropAnimation = animationCtrl
.create()
.addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', '0.01', '0.4');
const wrapperAnimation = animationCtrl
.create()
.addElement(baseEl.querySelector('.modal-wrapper')!)
.fromTo('transform', 'translateY(100%)', 'translateY(0)')
.fromTo('opacity', '0', '1');
return animationCtrl
.create()
.addElement(baseEl)
.easing('ease-out')
.duration(300)
.addAnimation([backdropAnimation, wrapperAnimation]);
};
// 使用自定义动画
const modal = await this.modalCtrl.create({
component: DetailModalComponent,
enterAnimation,
leaveAnimation: (baseEl) => enterAnimation(baseEl).direction('reverse')
});
3.2 导航方向控制 #
typescript
import { NavController } from '@ionic/angular';
// 前进导航
this.navCtrl.navigateForward('/detail');
// 后退导航
this.navCtrl.navigateBack('/home');
// 根页面
this.navCtrl.navigateRoot('/login');
四、导航性能优化 #
4.1 路由复用 #
typescript
// 路由复用策略
import { Injectable } from '@angular/core';
import { RouteReuseStrategy, ActivatedRouteSnapshot, DetachedRouteHandle } from '@angular/router';
@Injectable({
providedIn: 'root'
})
export class CustomRouteReuseStrategy implements RouteReuseStrategy {
private storedRoutes = new Map<string, DetachedRouteHandle>();
shouldDetach(route: ActivatedRouteSnapshot): boolean {
return route.data['reuse'] === true;
}
store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
this.storedRoutes.set(route.routeConfig?.path || '', handle);
}
shouldAttach(route: ActivatedRouteSnapshot): boolean {
return this.storedRoutes.has(route.routeConfig?.path || '');
}
retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
return this.storedRoutes.get(route.routeConfig?.path || '') || null;
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
return future.routeConfig === curr.routeConfig;
}
}
// 路由配置
const routes: Routes = [
{
path: 'home',
component: HomePage,
data: { reuse: true }
}
];
4.2 滚动位置恢复 #
typescript
// app-routing.module.ts
@NgModule({
imports: [
RouterModule.forRoot(routes, {
scrollPositionRestoration: 'enabled',
anchorScrolling: 'enabled'
})
]
})
export class AppRoutingModule {}
4.3 导航错误处理 #
typescript
import { Component } from '@angular/core';
import { Router, NavigationError } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent {
constructor(private router: Router) {
this.router.events.subscribe(event => {
if (event instanceof NavigationError) {
console.error('导航错误:', event.error);
this.router.navigate(['/error']);
}
});
}
}
五、高级导航模式 #
5.1 嵌套导航 #
typescript
// 父路由
const routes: Routes = [
{
path: 'settings',
component: SettingsPage,
children: [
{
path: '',
redirectTo: 'profile',
pathMatch: 'full'
},
{
path: 'profile',
component: ProfileSettingsComponent
},
{
path: 'security',
component: SecuritySettingsComponent
}
]
}
];
html
<!-- settings.page.html -->
<ion-header>
<ion-toolbar>
<ion-title>设置</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-split-pane>
<ion-menu>
<ion-list>
<ion-item routerLink="profile">个人资料</ion-item>
<ion-item routerLink="security">安全设置</ion-item>
</ion-list>
</ion-menu>
<ion-router-outlet></ion-router-outlet>
</ion-split-pane>
</ion-content>
5.2 命名路由出口 #
typescript
const routes: Routes = [
{
path: 'home',
component: HomePage,
children: [
{
path: 'detail/:id',
component: DetailComponent,
outlet: 'sidebar'
}
]
}
];
html
<ion-content>
<ion-router-outlet></ion-router-outlet>
<ion-router-outlet name="sidebar"></ion-router-outlet>
</ion-content>
5.3 动态路由 #
typescript
// 动态加载路由
import { Injector } from '@angular/core';
export class DynamicRouteService {
constructor(
private router: Router,
private injector: Injector
) {}
addRoute(path: string, component: any) {
const route = {
path,
component
};
this.router.config.push(route);
this.router.resetConfig(this.router.config);
}
}
六、最佳实践 #
6.1 路由组织 #
text
推荐结构:
├── /auth
│ ├── /auth/login
│ └── /auth/register
├── /tabs
│ ├── /tabs/home
│ ├── /tabs/search
│ └── /tabs/profile
├── /detail/:id
└── /settings
6.2 守卫使用 #
text
推荐:
- 认证守卫:CanActivate
- 权限守卫:CanActivateChild
- 表单守卫:CanDeactivate
- 数据预加载:Resolve
6.3 性能优化 #
text
推荐:
- 使用懒加载
- 合理使用预加载
- 路由复用
- 错误处理
七、总结 #
7.1 高级导航要点 #
| 要点 | 说明 |
|---|---|
| 路由守卫 | CanActivate、CanDeactivate、Resolve |
| 懒加载 | 模块按需加载 |
| 预加载 | 提前加载模块 |
| 导航动画 | 自定义过渡效果 |
| 性能优化 | 路由复用、错误处理 |
7.2 下一步 #
掌握了高级导航后,接下来让我们学习 HTTP请求,了解Ionic的数据处理!
最后更新:2026-03-28