Ionic导航组件 #

一、标签页组件(Tabs) #

1.1 基本用法 #

html
<!-- tabs.page.html -->
<ion-tabs>
  <ion-tab-bar slot="bottom">
    <ion-tab-button tab="home">
      <ion-icon name="home"></ion-icon>
      <ion-label>首页</ion-label>
    </ion-tab-button>
    
    <ion-tab-button tab="search">
      <ion-icon name="search"></ion-icon>
      <ion-label>搜索</ion-label>
    </ion-tab-button>
    
    <ion-tab-button tab="cart">
      <ion-icon name="cart"></ion-icon>
      <ion-label>购物车</ion-label>
      <ion-badge>3</ion-badge>
    </ion-tab-button>
    
    <ion-tab-button tab="profile">
      <ion-icon name="person"></ion-icon>
      <ion-label>我的</ion-label>
    </ion-tab-button>
  </ion-tab-bar>
</ion-tabs>

1.2 路由配置 #

typescript
// tabs.router.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { TabsPage } from './tabs.page';

const routes: Routes = [
  {
    path: '',
    component: TabsPage,
    children: [
      {
        path: 'home',
        loadChildren: () => import('../home/home.module').then(m => m.HomePageModule)
      },
      {
        path: 'search',
        loadChildren: () => import('../search/search.module').then(m => m.SearchPageModule)
      },
      {
        path: 'cart',
        loadChildren: () => import('../cart/cart.module').then(m => m.CartPageModule)
      },
      {
        path: 'profile',
        loadChildren: () => import('../profile/profile.module').then(m => m.ProfilePageModule)
      },
      {
        path: '',
        redirectTo: '/tabs/home',
        pathMatch: 'full'
      }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TabsPageRoutingModule {}

1.3 标签页样式 #

html
<!-- 顶部标签栏 -->
<ion-tabs>
  <ion-tab-bar slot="top">
    <!-- 标签按钮 -->
  </ion-tab-bar>
</ion-tabs>

<!-- 自定义样式 -->
<ion-tabs>
  <ion-tab-bar slot="bottom" class="custom-tab-bar">
    <ion-tab-button tab="home">
      <ion-icon name="home"></ion-icon>
      <ion-label>首页</ion-label>
    </ion-tab-button>
  </ion-tab-bar>
</ion-tabs>
scss
// 自定义标签栏样式
.custom-tab-bar {
  --background: #ffffff;
  --color: #999999;
  --color-selected: #3880ff;
  height: 60px;
  padding-bottom: env(safe-area-inset-bottom);
}

ion-tab-button {
  --color: #999999;
  --color-selected: #3880ff;
  --color-focused: #3880ff;
  
  ion-icon {
    font-size: 24px;
  }
  
  ion-label {
    font-size: 12px;
  }
}

1.4 标签页徽章 #

html
<!-- 带徽章的标签 -->
<ion-tab-button tab="notifications">
  <ion-icon name="notifications"></ion-icon>
  <ion-label>通知</ion-label>
  <ion-badge color="danger">5</ion-badge>
</ion-tab-button>

1.5 编程式切换 #

typescript
import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  constructor(private router: Router) {}
  
  goToCart() {
    this.router.navigate(['/tabs/cart']);
  }
  
  goToProfile() {
    this.router.navigateByUrl('/tabs/profile');
  }
}

二、侧边菜单组件(Menu) #

2.1 基本用法 #

html
<!-- app.component.html -->
<ion-app>
  <ion-menu side="start" contentId="main-content">
    <ion-header>
      <ion-toolbar>
        <ion-title>菜单</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <ion-list>
        <ion-item routerLink="/home">
          <ion-icon name="home" slot="start"></ion-icon>
          <ion-label>首页</ion-label>
        </ion-item>
        <ion-item routerLink="/settings">
          <ion-icon name="settings" slot="start"></ion-icon>
          <ion-label>设置</ion-label>
        </ion-item>
        <ion-item routerLink="/about">
          <ion-icon name="information-circle" slot="start"></ion-icon>
          <ion-label>关于</ion-label>
        </ion-item>
      </ion-list>
    </ion-content>
  </ion-menu>
  
  <ion-router-outlet id="main-content"></ion-router-outlet>
</ion-app>

2.2 菜单类型 #

html
<!-- 覆盖式菜单 -->
<ion-menu type="overlay" side="start" contentId="main-content">
  <!-- 菜单内容 -->
</ion-menu>

<!-- 推出式菜单 -->
<ion-menu type="push" side="start" contentId="main-content">
  <!-- 菜单内容 -->
</ion-menu>

<!-- 显示式菜单 -->
<ion-menu type="reveal" side="start" contentId="main-content">
  <!-- 菜单内容 -->
</ion-menu>

2.3 双侧菜单 #

html
<ion-app>
  <!-- 左侧菜单 -->
  <ion-menu side="start" menuId="left-menu" contentId="main-content">
    <ion-header>
      <ion-toolbar>
        <ion-title>左侧菜单</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <!-- 菜单项 -->
    </ion-content>
  </ion-menu>
  
  <!-- 右侧菜单 -->
  <ion-menu side="end" menuId="right-menu" contentId="main-content">
    <ion-header>
      <ion-toolbar>
        <ion-title>右侧菜单</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <!-- 菜单项 -->
    </ion-content>
  </ion-menu>
  
  <ion-router-outlet id="main-content"></ion-router-outlet>
</ion-app>

2.4 菜单控制 #

typescript
import { Component } from '@angular/core';
import { MenuController } from '@ionic/angular';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  constructor(private menuCtrl: MenuController) {}
  
  // 打开菜单
  openMenu() {
    this.menuCtrl.open('left-menu');
  }
  
  // 关闭菜单
  closeMenu() {
    this.menuCtrl.close();
  }
  
  // 切换菜单
  toggleMenu() {
    this.menuCtrl.toggle('left-menu');
  }
  
  // 启用/禁用菜单
  enableMenu(enable: boolean) {
    this.menuCtrl.enable(enable, 'left-menu');
  }
}

2.5 菜单按钮 #

html
<!-- 工具栏菜单按钮 -->
<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>标题</ion-title>
  </ion-toolbar>
</ion-header>

<!-- 指定菜单 -->
<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-menu-button menu="left-menu"></ion-menu-button>
    </ion-buttons>
    <ion-buttons slot="end">
      <ion-menu-button menu="right-menu"></ion-menu-button>
    </ion-buttons>
    <ion-title>标题</ion-title>
  </ion-toolbar>
</ion-header>

2.6 菜单样式 #

scss
ion-menu {
  --background: #ffffff;
  --width: 280px;
  
  ion-header {
    ion-toolbar {
      --background: #3880ff;
      --color: #ffffff;
    }
  }
  
  ion-item {
    --background: transparent;
    --color: #333333;
    
    &:hover {
      --background: #f5f5f5;
    }
  }
}

三、模态框组件(Modal) #

3.1 基本用法 #

typescript
import { Component } from '@angular/core';
import { ModalController } from '@ionic/angular';
import { DetailModalComponent } from './detail-modal/detail-modal.component';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  constructor(private modalCtrl: ModalController) {}
  
  async openModal() {
    const modal = await this.modalCtrl.create({
      component: DetailModalComponent,
      componentProps: {
        id: 1,
        title: '详情'
      }
    });
    
    await modal.present();
    
    const { data, role } = await modal.onWillDismiss();
    if (role === 'confirm') {
      console.log('确认:', data);
    }
  }
}

3.2 模态框组件 #

typescript
// detail-modal.component.ts
import { Component, Input } from '@angular/core';
import { ModalController } from '@ionic/angular';

@Component({
  selector: 'app-detail-modal',
  template: `
    <ion-header>
      <ion-toolbar>
        <ion-title>{{ title }}</ion-title>
        <ion-buttons slot="end">
          <ion-button (click)="close()">
            <ion-icon name="close"></ion-icon>
          </ion-button>
        </ion-buttons>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      <p>详情内容...</p>
      <ion-button expand="block" (click)="confirm()">确认</ion-button>
    </ion-content>
  `
})
export class DetailModalComponent {
  @Input() id!: number;
  @Input() title!: string;
  
  constructor(private modalCtrl: ModalController) {}
  
  close() {
    this.modalCtrl.dismiss(null, 'cancel');
  }
  
  confirm() {
    this.modalCtrl.dismiss({ result: 'success' }, 'confirm');
  }
}

3.3 模态框样式 #

typescript
// 全屏模态框
const modal = await this.modalCtrl.create({
  component: DetailModalComponent,
  cssClass: 'my-custom-modal'
});

// 底部弹出模态框
const modal = await this.modalCtrl.create({
  component: DetailModalComponent,
  breakpoints: [0, 0.5, 1],
  initialBreakpoint: 0.5
});

// 卡片式模态框
const modal = await this.modalCtrl.create({
  component: DetailModalComponent,
  presentingElement: await this.modalCtrl.getTop()
});
scss
// 自定义模态框样式
.my-custom-modal {
  --height: 80%;
  --width: 90%;
  --border-radius: 16px;
  --box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
}

3.4 模态框动画 #

typescript
const modal = await this.modalCtrl.create({
  component: DetailModalComponent,
  enterAnimation: myEnterAnimation,
  leaveAnimation: myLeaveAnimation
});

四、弹出框组件(Popover) #

4.1 基本用法 #

typescript
import { Component } from '@angular/core';
import { PopoverController } from '@ionic/angular';
import { MyPopoverComponent } from './my-popover/my-popover.component';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  constructor(private popoverCtrl: PopoverController) {}
  
  async showPopover(ev: any) {
    const popover = await this.popoverCtrl.create({
      component: MyPopoverComponent,
      event: ev,
      translucent: true
    });
    
    await popover.present();
    
    const { data } = await popover.onWillDismiss();
    console.log('选择:', data);
  }
}
html
<!-- 触发弹出框 -->
<ion-button (click)="showPopover($event)">
  显示选项
</ion-button>

4.2 弹出框组件 #

typescript
// my-popover.component.ts
import { Component } from '@angular/core';
import { PopoverController } from '@ionic/angular';

@Component({
  selector: 'app-my-popover',
  template: `
    <ion-list>
      <ion-item button (click)="select('edit')">
        <ion-icon name="create" slot="start"></ion-icon>
        <ion-label>编辑</ion-label>
      </ion-item>
      <ion-item button (click)="select('delete')">
        <ion-icon name="trash" slot="start" color="danger"></ion-icon>
        <ion-label color="danger">删除</ion-label>
      </ion-item>
      <ion-item button (click)="select('share')">
        <ion-icon name="share" slot="start"></ion-icon>
        <ion-label>分享</ion-label>
      </ion-item>
    </ion-list>
  `
})
export class MyPopoverComponent {
  constructor(private popoverCtrl: PopoverController) {}
  
  select(action: string) {
    this.popoverCtrl.dismiss({ action });
  }
}

4.3 弹出框位置 #

typescript
const popover = await this.popoverCtrl.create({
  component: MyPopoverComponent,
  event: ev,
  side: 'right',  // left, right, top, bottom
  alignment: 'center'  // start, center, end
});

4.4 弹出框样式 #

scss
ion-popover {
  --background: #ffffff;
  --box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
  --width: 200px;
  --border-radius: 8px;
}

五、导航控制器 #

5.1 页面导航 #

typescript
import { Component } from '@angular/core';
import { NavController } from '@ionic/angular';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  constructor(private navCtrl: NavController) {}
  
  // 前进导航
  goForward() {
    this.navCtrl.navigateForward('/detail/1');
  }
  
  // 后退导航
  goBack() {
    this.navCtrl.navigateBack('/home');
  }
  
  // 根页面
  goRoot() {
    this.navCtrl.navigateRoot('/login');
  }
  
  // 返回上一页
  goBackOne() {
    this.navCtrl.back();
  }
}

5.2 导航传参 #

typescript
// 发送页面
goToDetail() {
  this.navCtrl.navigateForward(['/detail', 1], {
    queryParams: {
      from: 'home',
      type: 'edit'
    }
  });
}

// 接收页面
import { ActivatedRoute } from '@angular/router';

export class DetailPage implements OnInit {
  id: string = '';
  from: string = '';
  
  constructor(private route: ActivatedRoute) {}
  
  ngOnInit() {
    this.id = this.route.snapshot.paramMap.get('id') || '';
    
    this.route.queryParams.subscribe(params => {
      this.from = params['from'];
    });
  }
}

六、导航栏组件 #

6.1 基本导航栏 #

html
<ion-header>
  <ion-toolbar>
    <ion-title>页面标题</ion-title>
  </ion-toolbar>
</ion-header>

6.2 带按钮的导航栏 #

html
<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-back-button defaultHref="/home"></ion-back-button>
    </ion-buttons>
    <ion-title>详情页</ion-title>
    <ion-buttons slot="end">
      <ion-button (click)="edit()">
        <ion-icon name="create" slot="icon-only"></ion-icon>
      </ion-button>
      <ion-button (click)="more()">
        <ion-icon name="ellipsis-vertical" slot="icon-only"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>
</ion-header>

6.3 搜索栏 #

html
<ion-header>
  <ion-toolbar>
    <ion-searchbar 
      placeholder="搜索..."
      (ionInput)="onSearch($event)"
      (ionCancel)="onCancel()">
    </ion-searchbar>
  </ion-toolbar>
</ion-header>

6.4 段选择器 #

html
<ion-header>
  <ion-toolbar>
    <ion-segment (ionChange)="onSegmentChange($event)">
      <ion-segment-button value="all">
        <ion-label>全部</ion-label>
      </ion-segment-button>
      <ion-segment-button value="active">
        <ion-label>进行中</ion-label>
      </ion-segment-button>
      <ion-segment-button value="completed">
        <ion-label>已完成</ion-label>
      </ion-segment-button>
    </ion-segment>
  </ion-toolbar>
</ion-header>

七、页面生命周期 #

7.1 Ionic生命周期 #

typescript
import { Component, OnInit } from '@angular/core';
import { 
  IonViewWillEnter, 
  IonViewDidEnter,
  IonViewWillLeave,
  IonViewDidLeave 
} from '@ionic/angular';

@Component({
  selector: 'app-detail',
  templateUrl: 'detail.page.html'
})
export class DetailPage implements OnInit {
  
  // Angular生命周期
  ngOnInit() {
    console.log('组件初始化');
  }
  
  ngOnDestroy() {
    console.log('组件销毁');
  }
  
  // Ionic生命周期
  ionViewWillEnter() {
    console.log('页面即将进入');
  }
  
  ionViewDidEnter() {
    console.log('页面已进入');
  }
  
  ionViewWillLeave() {
    console.log('页面即将离开');
  }
  
  ionViewDidLeave() {
    console.log('页面已离开');
  }
}

7.2 生命周期流程 #

text
页面导航流程:

进入页面:
ngOnInit → ionViewWillEnter → ionViewDidEnter

离开页面:
ionViewWillLeave → ionViewDidLeave → ngOnDestroy

返回页面:
ionViewWillEnter → ionViewDidEnter

八、最佳实践 #

8.1 导航结构 #

text
推荐结构:
├── tabs/
│   ├── home/
│   ├── search/
│   └── profile/
├── auth/
│   ├── login/
│   └── register/
└── detail/

8.2 懒加载 #

typescript
// 路由懒加载
const routes: Routes = [
  {
    path: 'detail/:id',
    loadChildren: () => import('./detail/detail.module')
      .then(m => m.DetailPageModule)
  }
];

8.3 路由守卫 #

typescript
// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(
    private auth: AuthService,
    private router: Router
  ) {}
  
  canActivate(): boolean {
    if (this.auth.isLoggedIn()) {
      return true;
    }
    this.router.navigate(['/login']);
    return false;
  }
}

// 路由配置
const routes: Routes = [
  {
    path: 'profile',
    loadChildren: () => import('./profile/profile.module')
      .then(m => m.ProfilePageModule),
    canActivate: [AuthGuard]
  }
];

九、总结 #

9.1 组件对比 #

组件 用途 特点
Tabs 底部标签导航 固定导航、徽章支持
Menu 侧边菜单 左右菜单、多种类型
Modal 模态框 全屏/半屏、数据传递
Popover 弹出框 上下文菜单、选项

9.2 下一步 #

掌握了导航组件后,接下来让我们学习 数据展示组件,了解Ionic的数据展示方式!

最后更新:2026-03-28