---
title: Tabs 标签页
description: 在同一区域切换多个内容面板,首次激活才挂载、切换不销毁
---
# Tabs 标签页
`Tabs` 在同一块区域里切换多个内容面板。它与 [Segmented](/ui/general/segmented) 的区别是:Tabs 负责「选择 + 内容区 + 懒渲染」,Segmented 只负责「选择」。
## 何时使用
- 同一对象的不同维度(基本信息 / 配置 / 权限)需要分区展示时;
- 面板内容较重(表格、图表、表单)时,Tabs 的懒渲染能避免一次性挂载所有面板;
- 纯视图模式切换、没有独立内容区时用 Segmented 更轻。
## 声明式写法
`G3TabPane` 只作声明,不渲染 DOM:`tab-key` 是唯一标识(不传按声明顺序取索引),`label` 是标签文字。
## 数据源写法
`items` 提供时优先于插槽;`content` 可以是字符串、VNode 或返回 VNode 的函数。标签动态来自接口时用这种写法。
## 位置
`placement="left" | "right"` 时标签栏纵向排列,指示条自动贴到对应边。
## 视觉风格
`type="line"` 是下划线指示条(默认),`type="pill"` 是胶囊式;`centered` 让标签栏居中。
## 只要标签栏
`navOnly` 时不渲染内容区,内容由外部(路由、分栏)控制;配合 `#label`、`#leftExtra`、`#rightExtra` 可以定制标签与两端内容。
## 键盘操作
标签栏是标准的 `role="tablist"`:`Tab` 进入标签栏后,用**左右键**(top 位置)或**上下键**(left / right 位置)切换,`Home` / `End` 跳首尾;禁用的标签会被跳过。
## 面板生命周期
面板**首次激活才挂载**,之后切换只切换显隐(`v-show`),因此:
- 表单输入、滚动位置、图表实例在切换后不会丢;
- 未访问过的面板不会发请求(适合「概览 / 明细有重请求」的场景);
- 如果面板里有需要「每次进入都刷新」的数据,请在 `change` 事件里自行处理。
## API
### Props(Tabs)
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `modelValue` | `string \| number` | — | 双向绑定的激活面板 key(内部按字符串比较,事件回传原始类型) |
| `placement` | `'top' \| 'left' \| 'right'` | `'top'` | 标签栏位置 |
| `type` | `'line' \| 'pill'` | `'line'` | 视觉风格 |
| `navOnly` | `boolean` | `false` | 只渲染标签栏,不渲染内容区 |
| `centered` | `boolean` | `false` | 标签栏居中 |
| `disabled` | `boolean` | `false` | 禁用整组 |
| `items` | `TabItem[]` | — | 数据源;提供且非空时优先于 `G3TabPane` 插槽 |
### Props(TabPane)
| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `tabKey` | `string \| number` | 声明顺序索引 | 唯一标识 |
| `label` | `string` | — | 标签文本(有 `#label` 插槽时忽略) |
| `disabled` | `boolean` | `false` | 禁用该标签 |
### Events(Tabs)
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `update:modelValue` | `(key: string \| number)` | 激活项变化(回传**原始类型**的 key) |
| `change` | `(key: string \| number)` | 仅在实际切换时触发(点当前标签不触发) |
| `tabClick` | `(key: string \| number, event: MouseEvent)` | 点击即触发(含点当前标签);键盘切换不触发 |
### Slots
| 名称 | 参数 | 说明 |
| --- | --- | --- |
| `default` | — | `G3TabPane` 列表(items 模式下可省) |
| `label` | `{ item, index }` | 自定义标签渲染 |
| `leftExtra` | — | 标签栏左端内容 |
| `rightExtra` | — | 标签栏右端内容 |
### 类型定义
```ts
export type TabsPlacement = 'top' | 'left' | 'right'
export type TabsType = 'line' | 'pill'
export type TabKey = string | number
export interface TabItem {
key: TabKey
label?: string | VNode
disabled?: boolean
content?: VNode | (() => VNode)
}
export interface TabPaneProps {
tabKey?: TabKey
label?: string
disabled?: boolean
}
```
### 样式变量
| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `--g3-tabs-tab-height` | `38px` | 标签项高度(故意高于控件档位,便于点击) |
| `--g3-color-primary` | `#18181b` | 激活项文字与指示条颜色 |
| `--g3-border-color` | `#d9d9d9` | 标签栏底部分隔线 |
## 实现说明
- **受控/非受控**:不传 `modelValue` 时内部维护激活项。若受控值指向不存在或已禁用的面板,会**回退显示第一个可用项,但不回写**——这样受控语义保持干净(否则会在渲染期偷偷改你的状态);
- **标签栏用原生 radio 语义吗?**不是:Tabs 用的是 `role="tablist"` + `role="tab"` + `role="tabpanel"` 与 roving tabindex(只有激活项 `tabindex=0`),键盘切换后主动 `focus()` 到目标标签,保证焦点可见;
- **懒渲染缓存**:`renderedKeys` 记录「访问过的 key」,模板用 `v-if` 控制挂载、`v-show` 控制显隐,因此切换不会重建 DOM,也就不会丢内部状态;
- 标签栏溢出时横向滚动(隐藏原生滚动条),标签不换行——避免出现两行标签把内容区挤下去;
- 指示条不使用 JS 测量:它就是每个标签自身的 `border-bottom-color`(left/right 位置对应 `border-right/left`),因此字体缩放、文案长度变化都不会错位,也不需要 `ResizeObserver`。