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