Ionic Tabs导航 #

一、Tabs概述 #

1.1 Tabs结构 #

text
Tabs结构
    │
    ├── Tabs容器
    │   ├── ion-tabs
    │   └── ion-tab-bar
    │
    ├── Tab按钮
    │   └── ion-tab-button
    │
    └── Tab页面
        ├── Tab1页面
        ├── Tab2页面
        └── Tab3页面

1.2 Tabs特点 #

特点 说明
底部导航 符合移动端习惯
状态保持 切换时保持页面状态
独立导航 每个Tab独立导航栈
徽章支持 可显示未读数量

二、基本Tabs配置 #

2.1 Tabs页面 #

html
<!-- tabs/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>

2.2 Tabs路由配置 #

typescript
// tabs/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'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/home',
    pathMatch: 'full'
  }
];

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

2.3 Tabs模块 #

typescript
// tabs/tabs.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { TabsPage } from './tabs.page';
import { TabsPageRoutingModule } from './tabs.router.module';

@NgModule({
  imports: [
    CommonModule,
    FormsModule,
    IonicModule,
    TabsPageRoutingModule
  ],
  declarations: [TabsPage]
})
export class TabsPageModule {}

三、Tab按钮配置 #

3.1 基本Tab按钮 #

html
<ion-tab-button tab="home">
  <ion-icon name="home"></ion-icon>
  <ion-label>首页</ion-label>
</ion-tab-button>

3.2 带徽章的Tab #

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>

3.3 动态徽章 #

html
<ion-tab-button tab="cart">
  <ion-icon name="cart"></ion-icon>
  <ion-label>购物车</ion-label>
  <ion-badge *ngIf="cartCount > 0" color="danger">
    {{ cartCount > 99 ? '99+' : cartCount }}
  </ion-badge>
</ion-tab-button>
typescript
import { Component } from '@angular/core';
import { CartService } from './cart.service';

@Component({
  selector: 'app-tabs',
  templateUrl: 'tabs.page.html'
})
export class TabsPage {
  cartCount = 0;
  
  constructor(private cartService: CartService) {
    this.cartService.cartCount$.subscribe(count => {
      this.cartCount = count;
    });
  }
}

3.4 自定义Tab图标 #

html
<ion-tab-button tab="home">
  <ion-icon name="home-outline" class="icon-outline"></ion-icon>
  <ion-icon name="home" class="icon-filled"></ion-icon>
  <ion-label>首页</ion-label>
</ion-tab-button>
scss
// 选中状态显示实心图标
ion-tab-button {
  .icon-filled {
    display: none;
  }
  
  &.tab-selected {
    .icon-outline {
      display: none;
    }
    
    .icon-filled {
      display: block;
    }
  }
}

四、Tab导航 #

4.1 编程式切换Tab #

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');
  }
}

4.2 Tab选择事件 #

html
<ion-tabs (ionTabsDidChange)="onTabChange($event)">
  <ion-tab-bar slot="bottom">
    <!-- tab buttons -->
  </ion-tab-bar>
</ion-tabs>
typescript
import { Component } from '@angular/core';

@Component({
  selector: 'app-tabs',
  templateUrl: 'tabs.page.html'
})
export class TabsPage {
  onTabChange(event: any) {
    console.log('当前Tab:', event.tab);
  }
}

4.3 Tab内导航 #

typescript
// 在Tab内导航到子页面
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) {}
  
  goToDetail(id: string) {
    // 导航到详情页(不在Tabs中)
    this.router.navigate(['/detail', id]);
  }
}

五、嵌套路由 #

5.1 Tab内子路由 #

typescript
// home/home-routing.module.ts
const routes: Routes = [
  {
    path: '',
    component: HomePage
  },
  {
    path: 'category/:id',
    component: CategoryPage
  },
  {
    path: 'product/:id',
    component: ProductPage
  }
];

5.2 子页面导航 #

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) {}
  
  goToCategory(id: string) {
    this.router.navigate(['/tabs/home/category', id]);
  }
  
  goToProduct(id: string) {
    this.router.navigate(['/tabs/home/product', id]);
  }
}

5.3 子页面返回 #

html
<!-- 子页面返回按钮 -->
<ion-header>
  <ion-toolbar>
    <ion-buttons slot="start">
      <ion-back-button defaultHref="/tabs/home"></ion-back-button>
    </ion-buttons>
    <ion-title>产品详情</ion-title>
  </ion-toolbar>
</ion-header>

六、Tabs样式定制 #

6.1 Tab栏样式 #

scss
// Tab栏背景
ion-tab-bar {
  --background: #ffffff;
  --color: #999999;
  --color-selected: #3880ff;
  height: 60px;
  padding-bottom: env(safe-area-inset-bottom);
  border-top: 1px solid #e0e0e0;
}

// Tab按钮样式
ion-tab-button {
  --color: #999999;
  --color-selected: #3880ff;
  --color-focused: #3880ff;
  --padding-top: 8px;
  --padding-bottom: 8px;
  
  ion-icon {
    font-size: 24px;
    margin-bottom: 4px;
  }
  
  ion-label {
    font-size: 12px;
  }
}

6.2 顶部Tab栏 #

html
<ion-tabs>
  <ion-tab-bar slot="top">
    <ion-tab-button tab="tab1">
      <ion-label>标签1</ion-label>
    </ion-tab-button>
    <ion-tab-button tab="tab2">
      <ion-label>标签2</ion-label>
    </ion-tab-button>
  </ion-tab-bar>
</ion-tabs>

6.3 自定义Tab栏 #

html
<ion-tabs>
  <ion-tab-bar slot="bottom" class="custom-tab-bar">
    <ion-tab-button tab="home" class="custom-tab-button">
      <div class="tab-icon">
        <ion-icon name="home"></ion-icon>
      </div>
      <ion-label>首页</ion-label>
    </ion-tab-button>
    
    <!-- 中间凸起按钮 -->
    <ion-tab-button tab="add" class="center-button">
      <div class="add-icon">
        <ion-icon name="add"></ion-icon>
      </div>
    </ion-tab-button>
    
    <ion-tab-button tab="profile" class="custom-tab-button">
      <div class="tab-icon">
        <ion-icon name="person"></ion-icon>
      </div>
      <ion-label>我的</ion-label>
    </ion-tab-button>
  </ion-tab-bar>
</ion-tabs>
scss
.custom-tab-bar {
  height: 70px;
  padding-bottom: env(safe-area-inset-bottom);
}

.center-button {
  --color: #ffffff;
  
  .add-icon {
    width: 50px;
    height: 50px;
    background: #3880ff;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: -25px;
    box-shadow: 0 4px 12px rgba(56, 128, 255, 0.3);
    
    ion-icon {
      font-size: 28px;
      color: #ffffff;
    }
  }
}

七、Tab状态管理 #

7.1 保持Tab状态 #

typescript
// Ionic默认保持Tab状态
// 每个Tab有独立的导航栈

// 首页
@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage implements OnInit {
  items: any[] = [];
  scrollPosition = 0;
  
  ngOnInit() {
    this.loadData();
  }
  
  ionViewWillLeave() {
    // 保存滚动位置
    this.scrollPosition = this.getScrollPosition();
  }
  
  ionViewWillEnter() {
    // 恢复滚动位置
    this.scrollToPosition(this.scrollPosition);
  }
}

7.2 Tab数据刷新 #

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

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html'
})
export class HomePage {
  // 每次进入Tab时刷新
  ionViewWillEnter() {
    this.refreshData();
  }
  
  async refreshData() {
    // 刷新数据
  }
}

八、最佳实践 #

8.1 Tabs数量 #

text
推荐:3-5个Tab
- 太少:浪费空间
- 太多:难以点击

8.2 Tab图标选择 #

text
推荐:
- 使用系统图标
- 保持风格一致
- 语义清晰

不推荐:
- 混用不同风格图标
- 使用过于复杂的图标

8.3 Tab标签文字 #

text
推荐:
- 简短明确(2-4个字)
- 统一字数
- 语义清晰

不推荐:
- 过长的标签文字
- 中英文混用

九、总结 #

9.1 Tabs要点 #

要点 说明
Tabs配置 ion-tabs + ion-tab-bar
Tab按钮 ion-tab-button
路由配置 子路由映射
嵌套路由 Tab内子页面
样式定制 CSS变量 + 自定义样式

9.2 下一步 #

掌握了Tabs导航后,接下来让我们学习 高级导航,了解Ionic的高级导航技巧!

最后更新:2026-03-28