--- 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`。