ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Vant 4 Tab / Tabs 标签页组件完全指南:API、源码原理与实战用法

Vant 4 Tab / Tabs 标签页组件完全指南:API、源码原理与实战用法 Vant 4 Tab / Tabs 标签页组件完全指南API、源码原理与实战用法【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vantTab标签页是 Vant 4 移动端组件库中最核心的导航类组件之一用于在有限空间内切换不同内容区域广泛覆盖“分类浏览、详情页分区、表单分步”等典型移动端交互场景。本文以 packages/vant/src/tab/README.md 为骨架结合 Tabs.tsx、Tab.tsx、TabTitle.tsx 等源码实现系统讲解 Tab / Tabs 的安装注册、全部使用场景、完整 API 参数、CSS 变量主题定制与常见 FAQ读完后你将能够在真实业务中熟练驾驭这一组件并理解其底层的工作原理。组件简介与安装注册Vant 4 的标签页功能由两个组件协作完成Tabs外层容器负责导航栏标题栏、底部指示线、滑动切换、粘性定位等整体行为Tab内层内容面板负责单个标签页的标题渲染与内容区渲染。二者必须搭配使用且Tab必须是Tabs的直接子组件。从源码 Tab.tsx 可以看到Tab在setup中通过useParent(TABS_KEY)查找父级Tabs如果找不到父组件会在非生产环境下输出警告[Vant] Tab must be a child component of Tabs.。安装与全局注册使用 npm 安装 Vant 后通过app.use全局注册组件即可import { createApp } from vue; import { Tab, Tabs } from vant; const app createApp(); app.use(Tab); app.use(Tabs);注册后模板中即可使用van-tabs与van-tab。除app.use外还支持按需引入、组件内局部注册等其他方式更多注册方式可参考仓库文档 advanced-usage.zh-CN.md。源码 tabs/index.ts 与 tab/index.ts 通过withInstall将组件挂载为带install方法的插件并同时声明了VanTabs、VanTab的全局组件类型便于 TypeScript 项目获得完整的模板类型提示。基础用法基础标签页默认激活第一个标签页通过v-model:active可以控制当前激活的标签页van-tabs v-model:activeactive van-tab v-forindex in 4 :titleTab index content of tab {{ index }} /van-tab /van-tabsimport { ref } from vue; export default { setup() { const active ref(0); return { active }; }, };v-model:active双向绑定的底层逻辑位于 Tabs.tsx当内部激活索引变化时组件会emit(update:active, newName)反过来当外部传入的active与当前实际激活名称不一致时又会通过setCurrentIndexByName校正内部状态。通过 name 匹配当标签页需要被外部稳定引用例如与路由、表单状态关联时建议为每个Tab指定唯一的name此时v-model:active绑定的是name而非索引van-tabs v-model:activeactiveName van-tab titleTab 1 nameacontent of tab 1/van-tab van-tab titleTab 2 namebcontent of tab 2/van-tab van-tab titleTab 3 nameccontent of tab 3/van-tab /van-tabsimport { ref } from vue; export default { setup() { const activeName ref(b); return { activeName }; }, };从源码看name的取值由 Tab.tsx 的getName()决定props.name ?? index.value即未显式指定name时默认使用索引Tabs侧则通过getTabNameTabs.tsx在children中查找匹配的标签页。滑动标签栏Swipe Tabs当标签数量较多默认超过 5 个时导航栏会自动变为可横向滚动van-tabs v-model:activeactive van-tab v-forindex in 8 :titleTab index content of tab {{ index }} /van-tab /van-tabs导航栏是否可滚动由 Tabs.tsx 中的scrollable计算属性决定当children.length swipeThreshold默认 5、或ellipsis为false、或shrink为true时导航栏进入可滚动模式。滚动模式下点击切换时会调用scrollIntoViewTabs.tsx通过 utils.ts 中基于requestAnimationFrame实现的scrollLeftTo平滑滚动将当前标签滚动到导航栏中央。通过swipe-threshold属性可以自定义触发滚动的标签数量阈值。禁用标签页使用disabled属性禁用单个标签页被禁用的标签不可点击、不可通过滑动选中van-tabs v-model:activeactive van-tab v-forindex in 3 :titleTab index :disabledindex 2 content of tab {{ index }} /van-tab /van-tabs卡片风格通过typecard将标签页切换为卡片风格van-tabs v-model:activeactive typecard van-tab v-forindex in 3 :titleTab index content of tab {{ index }} /van-tab /van-tabs卡片风格的颜色逻辑在 TabTitle.tsx 中实现当type card且设置了color时激活态使用color作为背景色非激活态使用color作为文字与边框颜色禁用状态下则保持默认样式。交互增强点击事件监听click-tab事件可以获取点击的标签信息name、title、原始event、是否禁用van-tabs v-model:activeactive click-tabonClickTab van-tab v-forindex in 2 :titleTab index content of tab {{ index }} /van-tab /van-tabsimport { showToast } from vant; export default { setup() { const onClickTab ({ title }) showToast(title); return { onClickTab, }; }, };注意click-tab与change的区别click-tab在每次点击标签时都会触发无论是否切换成功参数为{ name, title, event, disabled }类型定义见 types.tschange仅在激活标签真正发生变化时触发参数为(name, title)。粘性定位Sticky启用sticky后标签栏在滚动到页面顶部时会固定在顶部van-tabs v-model:activeactive sticky van-tab v-forindex in 4 :titleTab index content of tab {{ index }} /van-tab /van-tabs从源码看sticky 模式实际复用了 Vant 的Sticky组件Tabs.tsx并支持offset-top设置固定时距离顶部的偏移量单位支持px、vw、vh、rem默认px。sticky 状态下切换标签时组件还会通过setRootScrollTop校正页面滚动位置保证内容区与标题对齐。左对齐收缩Shrink默认情况下line风格的非可滚动导航栏会平均分配标签宽度启用shrink后标签会收缩并靠左排列van-tabs v-model:activeactive shrink van-tab v-forindex in 4 :titleTab index content of tab {{ index }} /van-tab /van-tabs van-tabs v-model:activeactive shrink typecard van-tab v-forindex in 4 :titleTab index content of tab {{ index }} /van-tab /van-tabs注意shrink会强制导航栏进入可滚动状态见 Tabs.tsx 中scrollable的第三个条件因此与ellipsis的文本省略逻辑互斥文档中明确注明ellipsis仅在shrink为false时生效。自定义标题使用title插槽可以完全自定义标题内容例如嵌入图标van-tabs v-model:activeactive van-tab v-forindex in 2 template #titlevan-icon namemore-o /Tab/template content of tab {{ index }} /van-tab /van-tabs自定义标题的渲染入口在 TabTitle.tsx当存在title插槽时优先渲染插槽内容否则渲染title文本同时会配合Badge组件渲染dot/badge标记。切换动画Animated启用animated后标签页内容切换将带有滑动动画van-tabs v-model:activeactive animated van-tab v-forindex in 4 :titleTab index content of tab {{ index }} /van-tab /van-tabs动画时长由duration属性控制默认0.3秒。手势滑动Swipeable启用swipeable后可以在内容区域通过左右滑动手势切换标签页van-tabs v-model:activeactive swipeable van-tab v-forindex in 4 :titleTab index content of tab {{ index }} /van-tab /van-tabsanimated与swipeable的底层实现都复用了Swipe组件从 TabsContent.tsx 可以看到二者开启时内容区会被包装进一个loop{false}、touchable的Swipe实例并禁用指示器duration会以毫秒为单位传入 Swipeprops.duration * 1000滑动切换后通过onChange回调更新当前索引。滚动监听Scrollspyscrollspy模式将内容面板改为平铺布局页面滚动到哪个内容块导航栏就高亮对应标签van-tabs v-model:activeactive scrollspy sticky van-tab v-forindex in 8 :titleTab index content of tab {{ index }} /van-tab /van-tabs其原理位于 Tabs.tsx组件监听滚动容器的scroll事件通过useScrollParent找到最近的滚动祖先用getCurrentIndexOnScroll根据各内容块距顶部的距离计算当前应激活的索引再调用setCurrentIndex更新。为避免滚动回调与程序化滚动互相干扰代码中使用lockScroll标志位加锁见scrollToCurrentContent。此外Tabs.tsx 暴露了scrollTo方法可在 scrollspy 模式下通过名称或索引跳转到指定标签页。切换前拦截Before Change通过before-change回调可以在标签切换前进行拦截返回false阻止切换也支持返回Promise进行异步校验van-tabs v-model:activeactive :before-changebeforeChange van-tab v-forindex in 4 :titleTab index content of tab {{ index }} /van-tab /van-tabsimport { ref } from vue; export default { setup() { const active ref(0); const beforeChange (index) { // prevent change if (index 1) { return false; } // async return new Promise((resolve) { setTimeout(() resolve(index ! 3), 1000); }); }; return { active, beforeChange, }; }, };Tips: 手势滑动切换不会触发 before-change 回调。底层实现中点击标签时会调用工具函数callInterceptorTabs.tsx先执行beforeChange若其返回true或Promise被 resolve 为true才会真正执行setCurrentIndex与scrollToCurrentContent若返回false则本次切换被阻止但click-tab事件仍然会照常派发。隐藏标题栏设置show-header为false可以隐藏标题栏此时完全由外部自定义组件控制active的取值van-tabs v-model:activeactive :show-headerfalse van-tab v-forindex in 4content of tab {{ index }}/van-tab /van-tabsshow-header自 Vant 4.7.3 版本开始提供源码中通过 Tabs.tsx 的条件渲染实现为false时整个导航栏包括 Sticky 包装都不会渲染。API 详解Tabs Props属性说明类型默认值v-model:active当前激活标签的名称或索引number | string0type样式风格可选line、cardstringlinecolor标签主题色string#1989fabackground标签栏背景色stringwhiteduration切换动画时长秒number | string0.3line-width底部指示线宽度number | string40pxline-height底部指示线高度number | string3pxanimated是否开启切换动画开启后内容区存在 sticky 布局时可能不符合预期booleanfalsebordertypeline时是否显示上下边框booleanfalseellipsis过长的标题是否省略仅在shrink为false且标签数小于等于swipe-threshold时生效booleantruesticky是否使用粘性定位booleanfalseshrink是否将标签收缩靠左排列booleanfalseswipeable是否开启内容区手势左右滑动开启后内容区存在 sticky 布局时可能不符合预期booleanfalselazy-render是否开启标签内容懒渲染booleantruescrollspy是否使用滚动监听模式booleanfalseshow-headerv4.7.3是否显示标题栏booleantrueoffset-top粘性定位时距离顶部偏移量支持pxvwvhrem单位默认pxnumber | string0swipe-threshold触发标签栏滚动的标签数量阈值仅在shrink为false且ellipsis为true时生效number | string5title-active-color标题激活态颜色string-title-inactive-color标题非激活态颜色string-before-change切换前的回调函数返回false阻止切换支持返回 Promise(name: number | string) boolean | Promiseboolean-补充说明源码依据type的取值类型TabsType line | card定义在 types.tsline-width/line-height通过 Tabs.tsx 的setLine方法计算指示线宽度以addUnit处理后赋值高度设置的同时会同步设置borderRadius实现胶囊形指示条line-width传入数字时默认按px处理color同时影响指示线颜色、卡片风格配色以及typeline时导航栏的borderColor见navStyleTabs.tsxanimated与swipeable均会触发内容区包裹Swipe二者在 FAQ 中都有关于 sticky 失效的说明详见下文。Tab Props属性说明类型默认值title标题文本string-disabled是否禁用该标签页booleanfalsedot标题上是否显示小红点booleanfalsebadge标题上徽标的内容dot为false时生效number | string-name标识符number | string标签索引url跳转链接string-to点击后跳转的目标路由与 Vue Router 的toprop 一致string | object-replace跳转时是否不留下历史记录booleanfalsetitle-style自定义标题样式string | Array | object-title-class自定义标题类名string | Array | object-show-zero-badge徽标值为 0 时是否显示booleantrue源码层面的细节url、to、replace由 use-route.ts 提供的routeProps混入在点击标签时通过route()完成路由跳转Tabs.tsx因此Tab天然支持链接与路由导航title-style/title-class在 Tab.tsx 中通过watchEffect配合normalizeClass/normalizeStyle进行标准化处理后透传给TabTitledot与badge最终通过Badge组件渲染TabTitle.tsxshow-zero-badge控制徽标为 0 时的显隐。Tabs Events事件说明参数click-tab点击标签时触发{ name: string | number, title: string, event: MouseEvent, disabled: boolean }change激活标签变化时触发name: string | number, title: stringrenderedlazy-render 模式下内容首次渲染时触发name: string | number, title: stringscrollsticky 模式下标签栏滚动时触发{ scrollTop: number, isFixed: boolean }rendered事件的触发链路在 Tab.tsx首次激活时init()置inited为true并在下一帧nextTick回调父级parent.onRendered(getName(), props.title)。Tabs Methods通过ref获取 Tabs 实例后可调用实例方法名称说明参数返回值resize容器尺寸变化或可见性变化时重算 Tabs 布局--scrollToscrollspy 模式下滚动到指定标签name: string | number-resize与scrollTo通过useExpose暴露Tabs.tsx。resize内部会重新计算指示线位置、将当前标签滚入视野并调用内部 Swipe 的resize方法Tabs.tsx此外组件在onActivatedKeepAlive 激活、onPopupReopen弹层重新打开以及useVisibilityChange页面可见性变化时都会自动触发setLine重算保证指示线始终对齐。类型定义组件导出了以下类型定义便于 TypeScript 项目使用import type { TabProps, TabsType, TabsProps, TabsInstance } from vant;TabsInstance是组件实例的类型典型用法如下import { ref } from vue; import type { TabsInstance } from vant; const tabsRef refTabsInstance(); tabsRef.value?.scrollTo(0);这些类型定义来源于 types.ts并从 tabs/index.ts 统一对外导出。SlotsTabs 插槽名称说明nav-left导航栏左侧自定义内容nav-right导航栏右侧自定义内容nav-bottom导航栏底部自定义内容Tab 插槽名称说明default标签页内容title自定义标题从 Tabs.tsx 的renderHeader可以看到nav-left/nav-right分别渲染在标题列表的两侧nav-bottom渲染在标题栏整体下方Tab的title插槽则通过renderTitle透传给TabTitleTab.tsx。主题定制CSS 变量Tab / Tabs 提供了以下 CSS 变量用于自定义样式可与ConfigProvider组件配合使用参见 config-provider名称默认值说明--van-tab-text-colorvar(--van-gray-7)标题默认文字颜色--van-tab-active-text-colorvar(--van-text-color)标题激活态文字颜色--van-tab-disabled-text-colorvar(--van-text-color-3)标题禁用态文字颜色--van-tab-font-sizevar(--van-font-size-md)标题字号--van-tab-line-heightvar(--van-line-height-md)标题行高--van-tabs-default-colorvar(--van-primary-color)默认主题色--van-tabs-line-height44px标签栏高度--van-tabs-card-height30px卡片风格标签高度--van-tabs-nav-backgroundvar(--van-background-2)标签栏背景--van-tabs-bottom-bar-width40px底部指示线宽度--van-tabs-bottom-bar-height3px底部指示线高度--van-tabs-bottom-bar-colorvar(--van-primary-color)底部指示线颜色这些变量的完整 TypeScript 类型定义TabsThemeVars位于 types.ts实际样式实现在 tab/index.less 中。若通过ConfigProvider传入theme-vars需要注意变量名需要去掉--van-前缀并使用小驼峰写法。常见问题FAQ启用 swipeable 或 animated 后内容区元素的 sticky 功能不符合预期当Tabs启用swipeable或animated后内容区会被带有transform属性的元素包裹此时内容区内的元素如果启用了sticky功能虽然仍会生效但显示位置不会符合预期。例如van-tabs v-model:activeactive swipeable van-tab van-sticky van-buttonsticky button/van-button /van-sticky /van-tab /van-tabs这是因为transform元素内部的fixed定位是相对该元素计算的而不是相对整个文档从而产生布局异常。本质原因是animated/swipeable模式下内容被Swipe的轨道容器包裹见 TabsContent.tsxtransform会建立新的包含块。解决方案是避免在同一Tabs上同时启用这些组合或将sticky元素放在内容区之外。如何判断当前组件是否位于激活的 Tab 内在子组件中可以使用useTabStatus或useAllTabStatus判断组件是否位于激活的Tab内useTabStatus返回当前组件所在的父级Tab是否激活若组件不在Tab内返回null。useAllTabStatus用于嵌套 Tab 场景返回所有父级Tabs是否同时激活若组件不在Tab内返回null。const isActive useTabStatus(); // 嵌套 Tab 场景下使用 const isAllActive useAllTabStatus();这两个 hook 的实现位于 use-tab-status.tsTab在渲染时通过useProvideTabStatus(active)提供响应式激活状态Tab.tsxuseAllTabStatus会在嵌套场景下逐层向上合并所有父级Tab的激活状态只有全部激活时才返回true。它们从 tab/index.ts 对外导出可用于在子组件中实现“仅在可见时执行某些副作用如埋点、数据加载”之类的业务逻辑。小结Tab / Tabs 是 Vant 4 中功能最完整的复合组件之一既有开箱即用的基础切换能力v-model:active、name匹配、禁用、卡片风格又覆盖了移动端常见的高级交互滑动标签栏、sticky 吸顶、shrink 收缩、animated 动画、swipeable 手势、scrollspy 滚动监听、before-change 拦截、自定义标题与徽标同时通过完善的 CSS 变量体系支持深度主题定制。理解 Tabs.tsx 中setLine、scrollIntoView、setCurrentIndex等核心方法以及 Tab.tsx 中父子组件协作的useParent/useProvideTabStatus机制将帮助你在复杂业务场景中准确地使用和扩展这一组件。【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表