☰
Varlet TabItem 视图面板组件完全指南:name 属性、default 插槽与 TabsItems 联动原理
2026/10/14 14:43:30 网站建设 项目流程
  • UI组件
  • 前端

【免费下载链接】varlet

A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.

项目地址:https://gitcode.com/gh_mirrors/va/varlet
点击查看免费下载

TabItem 是 Varlet(基于 Vue 3、Material Design 2/3 的组件库)中负责展示选项卡对应视图内容的面板组件,它必须与TabsItems容器配合使用,与Tabs/Tab一起构成完整的选项卡联动方案。本文将完整讲解 TabItem 的name属性与default插槽用法,并从源码层面剖析其激活匹配、滑动切换与布局实现的底层原理,帮助你写出可直接运行的选项卡视图联动代码。

组件定位:选项卡体系中的"视图面板"角色

在 Varlet 的选项卡家族中,四个组件各司其职:

  • Tabs:选项卡组容器,管理激活态与指示器;
  • Tab:单个选项卡(按钮),负责被点击与高亮;
  • TabsItems:视图容器,基于 Swipe 组件实现滑动切换;
  • TabItem:单个视图面板,负责承载一个选项卡对应的内容。

正如 Tabs 文档 所述:"使用Tabs、Tab实现选项卡组的切换。使用TabsItems、TabItem实现和选项卡组的视图联动。" TabItem 是整个联动链条的末端,直接决定每个页面展示什么内容。从源码结构看,TabItem.vue 的模板只做了一件事——把默认插槽内容包进一个var-swipe-item:

<var-swipe-item :class="classes(n(), [!current, n('--inactive')])" var-tab-item-cover> <slot /> </var-swipe-item>

也就是说,TabItem 本身不负责"切换逻辑",切换由外层TabsItems统一调度,TabItem 只负责上报自身状态(索引、名字、是否激活)并渲染内容。

API 概览:属性与插槽

TabItem 的官方 API 定义非常精简,zh-CN 文档 完整内容如下:

属性

参数说明类型默认值
name视图的名字string | number-

插槽

插槽名说明参数
default视图的内容-

与文档对应,props.ts 中name的类型声明为[String, Number],即既可以是字符串也可以是数字;未设置时默认值为空(-)。类型声明可以在 types/tabItem.d.ts 中进一步确认:

export interface TabItemProps extends BasicAttributes { name?: string | number }

default插槽没有任何作用域参数,直接书写任意模板内容即可,支持指令、组件与纯文本。

基础使用:与 TabsItems 实现视图联动

单独使用 TabItem 没有意义,它必须放置在TabsItems内部(源码中对此有强制断言,详见下文"实现原理")。下面这段来自 Tabs 文档"视图联动"示例 的代码,是 TabItem 最标准的用法——Tabs、Tab与TabsItems、TabItem共用同一个active响应式变量,点击选项卡即可切换对应视图,同时支持在视图上左右滑动切换:

<script setup> import { ref } from 'vue' const active = ref(0) </script> <template> <var-tabs elevation color="var(--color-primary)" active-color="var(--color-on-primary)" inactive-color="var(--color-on-info)" v-model:active="active" > <var-tab>选项1</var-tab> <var-tab>选项2</var-tab> <var-tab>选项3</var-tab> </var-tabs> <var-tabs-items v-model:active="active"> <var-tab-item> 呜啦啦啦火车笛,随着奔腾的马蹄。 小妹妹吹着口琴,夕阳下美了剪影。 </var-tab-item> <var-tab-item> 很多人不长眼睛,嚣张都靠武器。 赤手空拳就缩成蚂蚁。 </var-tab-item> <var-tab-item> 你们一起上,我在赶时间。 每天决斗观众都累了,英雄也累了。 </var-tab-item> </var-tabs-items> </template>

也可以参考仓库内真实的 Tabs 示例页,其中通过activeRelation变量将一组Tabs与一组TabsItems绑定,并在TabItem中渲染多行文本内容。

结合 name 使用具名视图

name属性用于给视图命名,便于以稳定的标识(而不是脆弱的索引)来绑定激活状态。当active与name对应时,即使视图顺序发生调整,联动依然准确。例如:

<script setup> import { ref } from 'vue' const active = ref('overview') </script> <template> <var-tabs v-model:active="active"> <var-tab name="overview">总览</var-tab> <var-tab name="detail">详情</var-tab> </var-tabs> <var-tabs-items v-model:active="active"> <var-tab-item name="overview">总览视图内容</var-tab-item> <var-tab-item name="detail">详情视图内容</var-tab-item> </var-tabs-items> </template>

name 属性的匹配机制:优先 name,其次 index

TabItem 的name到底如何参与激活匹配?答案在 TabsItems.vue 的匹配函数中:

function matchName(active: number | string | undefined): TabItemProvider | undefined { return tabItemList.find(({ name }: TabItemProvider) => active === name.value) } function matchIndex(active: number | string | undefined): TabItemProvider | undefined { return tabItemList.find(({ index }: TabItemProvider) => active === index.value) } function matchActive(active: number | string | undefined): TabItemProvider | undefined { return matchName(active) || matchIndex(active) }

可以看到匹配策略是:优先按name匹配,匹配不到再回退到按index(即 TabItem 在 TabsItems 中的注册顺序)匹配。这与 Tabs 文档中"Tabs 默认通过 active 去匹配 Tab 的 index……您也可以通过给 Tab 设置 name 来命名选项卡,这时 Tabs 会优先匹配 name"的说明完全一致,只是该策略在 TabItem 侧同样生效。

当active变化时,handleActiveChange会先让所有 TabItem 失活,再激活匹配到的那一个,并驱动内部 Swipe 切换到对应位置:

tabItemList.forEach(({ setCurrent }) => setCurrent(false)) newActiveTabItemProvider.setCurrent(true) swipe.value?.to(newActiveTabItemProvider.index.value)

反过来,当用户直接在视图上滑动切换时,handleSwipeChange会读取当前视图的name(优先)或index(兜底)并反向回写active:

const tabItem = tabItemList.find(({ index }) => index.value === currentIndex) as TabItemProvider const active = tabItem.name.value ?? tabItem.index.value call(props['onUpdate:active'], active)

这一来一回,正是"点击选项卡切视图、滑动视图同步选项卡"双向联动的核心链路。

源码级原理解析

1. 组件实现:基于 SwipeItem 的薄封装

TabItem.vue 的setup中,组件通过useTabsItems()向父级TabsItems注册自己,并暴露一个TabItemProvider接口:

const tabItemProvider: TabItemProvider = { index, name, current: computed(() => current.value), setCurrent, } bindTabsItems(tabItemProvider) bindLists(tabItemProvider)

其中current标记该视图是否激活,setCurrent由父级调用以切换激活态。同时组件还通过useLists()与List组件体系建立关联(TAB_ITEM_BIND_LIST_KEY),这是 TabItem 支持与 List 组件组合的扩展点。

2. 强制约束:必须位于 var-tabs-items 内

provide.ts 中的useTabsItems通过@varlet/use的useParent向上查找TABS_ITEMS_BIND_TAB_ITEM_KEY对应的父级 Provider,并带有明确的运行时断言:

assert(!!bindParent, 'TabItem', '<var-tab-item/> must in <var-tabs-items/>')

也就是说,若将<var-tab-item>直接放到页面顶层或Tabs内部,运行时会抛出 "<var-tab-item/> must in <var-tabs-items/>" 的提示,这是组件设计上的硬性约束。

3. 样式细节:非激活视图高度归零

tabItem.less 定义了非激活视图的布局策略:

.var-tab-item { &--inactive[var-tab-item-cover] { overflow: visible; height: 0; } }

当 TabItem 处于非激活状态时,通过height: 0折叠内容、overflow: visible允许内容溢出显示,从而让视图切换在视觉上表现为"内容随滑动进出",同时避免非激活视图占据布局空间。

4. 注册与类型:withInstall 插件化安装

index.ts 中组件通过withInstall与withPropsDefaultsSetter完成插件化封装,既可app.use(TabItem)全量注册,也可按需引入:

withInstall(TabItem) withPropsDefaultsSetter(TabItem, tabItemProps) export { tabItemProps } export const _TabItemComponent = TabItem export default TabItem

配套组件属性:掌握 TabsItems 才能用好 TabItem

由于 TabItem 的行为完全由外层TabsItems驱动,理解TabsItems的属性才能充分发挥 TabItem 的能力。根据 TabsItems 文档 与 props.ts,其属性如下:

参数说明类型默认值
v-model:active激活的选项卡的名字(与 TabItem 的 name/index 对应)string | number0
can-swipe是否允许在视图上滑动切换booleantrue
animated是否启用内容切换动画booleantrue
loop是否允许循环切换booleanfalse

值得注意的实现细节:animated通过控制 Swipe 的duration生效(为false时 duration 传0),can-swipe映射到 Swipe 的touchable,loop直接透传给 Swipe;此外TabsItems内部会强制隐藏 Swipe 的指示器(:indicator="false"),因为指示器语义已由上方Tabs承担。

TabsItems还暴露了getSwipe()方法,可获取内部swipe组件实例,用于需要精细控制切换的进阶场景。这一行为由仓库测试 tabs/tests/index.spec.js 验证:测试挂载TabsItems与TabItem后断言wrapper.vm.getSwipe()存在,且调用getSwipe().next()会触发onUpdate:active。同一测试文件也验证了四个组件均可通过app.use()注册。

总结与使用建议

围绕 TabItem 这枚"视图面板",可以归纳出几条可直接落地的实践要点:

  1. 成对使用:TabItem只能放在TabsItems中,二者与Tabs、Tab组成四件套,共用同一个v-model:active变量实现双向联动。
  2. 善用 name:当视图顺序可能变化、或希望以业务语义(如路由名、数据键)驱动激活时,为 TabItem 设置name;匹配时优先name、兜底index。
  3. 自由填充:default插槽没有作用域参数,任意模板内容都可直接放入;配合can-swipe、animated、loop属性可控制手势与动画行为。
  4. 按需引入:TabItem 已通过withInstall封装,支持全量注册与按需引入,类型声明见 types/tabItem.d.ts。

如果想要看到 TabItem 更多真实运行效果与组合用法,可以直接阅读仓库中的 Tabs 示例页 与 Tabs 文档,其中包含了视图联动、粘性布局、指示器反转等完整可运行示例。

  • UI组件
  • 前端

【免费下载链接】varlet

A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.

项目地址:https://gitcode.com/gh_mirrors/va/varlet
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询