Vuetify 时间轴组件 VTimeline 完整指南:垂直/水平时间线的配置、插槽与源码实现
【免费下载链接】vuetify🐉 Vue Component Framework项目地址: https://gitcode.com/gh_mirrors/vu/vuetify
v-timeline是 Vuetify 中用于以时间顺序(垂直或水平方向)展示信息的组件。本文将围绕 Vuetify 仓库内 VTimeline 组件文档(timelines.md)为主线,完整覆盖direction、side、align、dot-color、size、truncate-line、line-inset等全部核心属性,以及icon、opposite插槽的实战用法,并深入其源码(VTimeline.tsx、VTimelineItem.tsx、VTimelineDivider.tsx)讲解默认值传递与 CSS 变量机制,帮助读者从"会用"进阶到"懂原理"。
概述:什么是 v-timeline
时间轴(Timeline)是 Web 应用中展示"按时间顺序发生的事件"的经典视觉形式,常见于订单物流进度、项目里程碑、操作历史记录、产品迭代日志等场景。Vuetify 的v-timeline组件正是为此设计,它通过一条贯穿始终的中心线串联起一个个事件节点(圆点 + 内容块),并以优雅的网格布局处理左右两侧内容的排布。
从组件职责看,VTimeline 家族包含两个对外暴露的组件:
| 组件 | 作用 | | - | - | |v-timeline| 主组件,负责整体的方向、对齐、侧边、线宽等布局配置 | |v-timeline-item| 子组件,用于展示单个时间轴条目(内容、圆点、对侧内容) |
此外,源码内部还有一个VTimelineDivider(分隔器)组件,负责渲染每个条目的"前段线、圆点、后段线",它不直接对外使用,但在理解圆点样式机制时非常关键。
快速上手:最简单的用法
v-timeline最简形态是一个垂直时间轴,内部至少包含一个v-timeline-item:
<template> <v-timeline> <v-timeline-item> <div> <div class="text-title-large">Content title</div> <p> 事件描述内容,例如"完成需求评审并输出交互稿"。 </p> </div> </v-timeline-item> </v-timeline> </template>默认情况下,内容块与圆点居中排布在中心线两侧,圆点使用主题色呈现。仓库中对应的最小示例可参考 usage.vue,文档站点的完整用例组织在 v-timeline 示例目录 下。
API 一览:v-timeline 与 v-timeline-item
v-timeline 核心属性
主组件在 VTimeline.tsx 中通过propsFactory定义属性,主要如下:
| 属性 | 类型 | 默认值 | 说明 | | - | - | - | - | |direction|'vertical' \| 'horizontal'|'vertical'| 时间轴方向,垂直或水平 | |align|'center' \| 'start'|'center'| 条目内容在交叉轴上的对齐方式 | |side|'start' \| 'end'| 未设置 | 将所有条目强制放到时间轴的一侧 | |justify|'auto' \| 'center'|'auto'| 内容在主轴上的对齐方式 | |truncate-line|'start' \| 'end' \| 'both'| 未设置 | 截断中心线的起始端、末端或两端 | |line-thickness|string \| number|2| 中心线粗细(px) | |line-color|string| 未设置 | 中心线颜色 | |density|'default' \| 'compact'|'default'| 密度,compact会隐藏对侧内容区 | |dot-color|string| 未设置 | 全部圆点的颜色(可被 item 覆盖) | |fill-dot|boolean|false| 是否让圆点充满整个分隔区域 | |hide-opposite|boolean| 未设置 | 是否隐藏对侧内容 | |icon-color|string| 未设置 | 圆点内图标的颜色 | |line-inset|number \| string|0| 中心线的内缩量(见下文"Line inset") | |size|string \| number| 未设置 | 圆点尺寸,支持x-small至x-large及数值 |
注意:
dot-color、fill-dot、hide-opposite、icon-color、line-inset、size原本是v-timeline-item的属性,VTimeline通过...pick(makeVTimelineItemProps(...), [...])将其挑选出来并在根组件上暴露(VTimeline.tsx),再通过provideDefaults统一下发到每个 item——这就是"在根组件上设置一次、作用于全部条目"的实现基础。
v-timeline-item 属性
v-timeline-item在 VTimelineItem.tsx 中定义,除继承上述属性外,还独有以下能力:
| 属性 | 类型 | 说明 | | - | - | - | |icon|IconValue| 圆点内显示的图标 | |hide-dot|boolean| 隐藏该条目的圆点 | |side|'start' \| 'end'| 单条条目强制放在某一侧(优先级高于根组件的side) | |elevation| 数字 | 圆点的阴影等级 | |rounded| 字符串 | 圆点的圆角样式(默认圆形) | |dimension| 数字/字符串 | 内容主体的宽度约束 |
v-timeline-item还支持density="compact":此时渲染逻辑会跳过对侧内容容器(VTimelineItem.tsx),得到更紧凑的行高。
方向:垂直与水平切换(direction)
通过direction属性可以在垂直与水平时间轴之间实时切换:
<v-timeline direction="horizontal"> <v-timeline-item v-for="i in 3" :key="i"> <template v-slot:opposite> Opposite content </template> <div> <div class="text-title-large">Content title</div> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit... </p> </div> </v-timeline-item> </v-timeline>垂直时间轴时中心线自上而下、条目左右交错;切换为horizontal后,中心线变为横向贯穿、条目沿水平方向依次排开。渲染根元素时组件会附加v-timeline--vertical/v-timeline--horizontal类(VTimeline.tsx),布局交由 Sass 中对应的网格模板完成。完整示例见 prop-direction.vue。
单侧排列:side 属性
默认垂直时间轴下,条目内容交替出现在中心线两侧;若希望所有条目统一放在一侧(例如左侧放内容、右侧留给大段空白或配套说明),使用side属性即可:
<v-timeline side="end"> <v-timeline-item v-for="item in items" :key="item.id" :dot-color="item.color" size="small" > <v-alert :color="item.color" :icon="item.icon" :value="true"> Lorem ipsum dolor sit amet, no nam oblique veritus... </v-alert> </v-timeline-item> </v-timeline>示例数据可配合v-alert构建"状态提醒"型时间轴(如通知中心),完整代码见 prop-single-side.vue。
源码中有一个容易忽略的默认行为:当side未设置、但density不是default时,组件会自动把侧边设为end(VTimeline.tsx),最终通过v-timeline--side-start/v-timeline--side-end类生效。RTL(从右到左)环境下,useRtl提供的rtlClasses会自动翻转侧边语义。
对齐方式:align
v-timeline-item内容在默认情况下是垂直居中(center)于圆点的;当内容块很高、希望它们顶部对齐时,设置align="start":
<v-timeline align="start"> <v-timeline-item> <template v-slot:opposite> Opposite content </template> <div> <div class="text-title-large">Content title</div> <p>...</p> </div> </v-timeline-item> <!-- 更多条目 --> </v-timeline>组件会据此渲染v-timeline--align-start类(VTimeline.tsx),改变条目的网格对齐方式。示例见 prop-align.vue。
圆点样式:颜色与图标
彩色圆点(dot-color)
不同颜色的圆点能形成视觉断点,帮助用户快速区分事件类型。比如用"日程表"风格:粉色圆点表示休息/里程碑、青色圆点表示工作项:
<v-timeline align="start" side="end"> <v-timeline-item dot-color="pink" size="small"> <div class="d-flex"> <strong class="me-4">5pm</strong> <div> <strong>New Icon</strong> <div class="text-body-small">Mobile App</div> </div> </div> </v-timeline-item> <v-timeline-item dot-color="teal-lighten-3" size="small"> <div class="d-flex"> <strong class="me-4">3-4pm</strong> <div> <strong>Design Stand Up</strong> <div class="text-body-small mb-2">Hangouts</div> </div> </div> </v-timeline-item> </v-timeline>dot-color接受任何 Vuetify 主题色(pink、teal-lighten-3等),也支持在根组件v-timeline上统一设置。完整示例见 prop-color.vue。
图标圆点(icon dots)
在圆点内放置图标可以补充语义信息(如mdi-star、mdi-book-variant),配合fill-dot让圆点填满分隔区域,再叠加v-card呈现条目主体:
<v-timeline align="start"> <v-timeline-item v-for="(item, i) in items" :key="i" :dot-color="item.color" :icon="item.icon" fill-dot > <v-card> <v-card-title :class="['text-title-large', `bg-${item.color}`]"> Lorem Ipsum Dolor </v-card-title> <v-card-text class="bg-white text--primary"> <p>...</p> <v-btn :color="item.color" variant="outlined">Button</v-btn> </v-card-text> </v-card> </v-timeline-item> </v-timeline>示例数据(红/紫/绿/靛蓝四色配四个图标)与完整结构见 prop-icon-dots.vue。从源码看,图标最终由VTimelineDivider内部的VIcon渲染,并继承iconColor、size(VTimelineDivider.tsx)。
圆点尺寸:size
size属性可以精确控制每个圆点的大小,取值既可以是 Vuetify 尺寸令牌(x-small、small、default、large、x-large),也可以是具体像素数值。结合不同颜色构建"多尺寸时间轴"非常直观,例如大圆点 + 图标卡片、小圆点 + 简短文本交替排列:
<v-timeline> <v-timeline-item dot-color="purple-lighten-2" fill-dot> <v-card>...</v-card> </v-timeline-item> <v-timeline-item dot-color="amber-lighten-1" size="x-small" fill-dot> <v-card>...</v-card> </v-timeline-item> </v-timeline>完整五条目的示例见 prop-size.vue。在 VTimelineDivider.tsx 中,useSize(props, 'v-timeline-divider__dot')会把尺寸映射为v-timeline-divider__dot--x-small等类与对应样式;Sass 变量表中还定义了各尺寸下内圆点与外圆点之间的边框粗细($timeline-dot-border-sizes,见 _variables.scss)。
中心线控制:truncate-line 与 line-inset
截断中心线(truncate-line)
当时间轴只是某个更大页面中的片段时,中心线首尾两端多余的"断头线"会影响美观。truncate-line支持:
start:截断起始端end:截断末端both:两端都截断
<v-timeline truncate-line="both"> <v-timeline-item>...</v-timeline-item> </v-timeline>源码中通过v-timeline--truncate-line-start/v-timeline--truncate-line-end类控制(VTimeline.tsx),Sass 层面对应隐藏相应分隔器的before/after线段。示例见 prop-truncate-line.vue。
中心线内缩(line-inset)
line-inset允许你指定一个自定义数值,让中心线相对圆点中心向内收缩,常用于让线段贴合圆点边缘而非贯穿圆点:
<v-timeline line-inset="4"> <v-timeline-item>...</v-timeline-item> </v-timeline>从 VTimelineItem.tsx 的实现可以看到它的计算公式:
--v-timeline-line-inset: calc(var(--v-timeline-dot-size) / 2 + <lineInset>)即内缩量 = 圆点半径 + 你指定的附加值;同时根组件在lineInset非 0 时会附加v-timeline--inset-line类(VTimeline.tsx)。示例见 prop-line-inset.vue。
插槽:icon 与 opposite
v-timeline-item暴露了三个插槽:默认插槽(内容主体)、icon(替换圆点内部内容)、opposite(对侧内容)。
icon 插槽:把头像放进圆点
icon插槽允许完全自定义圆点内部内容。最典型的用法是结合v-avatar把用户头像放进圆点,非常适合"操作记录 / 动态流"场景:
<v-timeline> <v-timeline-item> <template v-slot:icon> <v-avatar> <v-img src="/path/to/avatar.png"></v-img> </v-avatar> </template> <v-card>...</v-card> </v-timeline-item> </v-timeline>源码中,当icon插槽被提供时,VTimelineDivider会改用VDefaultsProvider包裹插槽内容,并仍将iconColor、icon、size作为VIcon的默认值注入(VTimelineDivider.tsx),因此插槽内的v-icon无需重复传色。示例见 slot-icon.vue。
opposite 插槽:对侧内容
opposite插槽用于在中心线另一侧放置补充信息,为时间轴提供额外的自定义层次。例如前面方向示例中,每个条目都在对侧渲染 "Opposite content":
<v-timeline-item> <template v-slot:opposite> <span class="text-caption">2026-09-18</span> </template> <v-card>事件主体内容</v-card> </v-timeline-item>网格布局(见下文)会为 opposite 内容自动分配与主体等宽的一列,因此非常适合放置时间戳、持续时间等元数据。完整示例见 slot-opposite.vue。注意:density="compact"或hide-opposite时对侧区域不会渲染。
综合案例:misc-advanced
文档还提供了一个综合示例 misc-advanced.vue,它组合运用了上文介绍的方向/对齐、图标圆点、fill-dot、v-card主体与opposite插槽,呈现一份接近生产形态的完整时间轴。建议读者把它作为"最终拼装"的参考模板:先确定direction与side,再统一dot-color/size节奏,最后用icon或opposite插槽补齐细节信息。
源码实现原理:组件结构、默认值传递与样式体系
组件分层:VTimeline / VTimelineItem / VTimelineDivider
VTimeline 家族采用三层结构:
VTimeline(根):只负责输出容器标签与各类修饰类名,不直接渲染条目;VTimelineItem:每个条目渲染为v-timeline-item,内部包含v-timeline-item__body(默认内容)、VTimelineDivider(圆点+线)、v-timeline-item__opposite(对侧内容)三个区块;VTimelineDivider:渲染before(前段线)、dot(圆点,内部再套inner-dot)、after(后段线)三段结构(VTimelineDivider.tsx)。
值得一提的是,VTimelineItem会通过watch监听 Divider 的挂载,读取.v-timeline-divider__dot的实际渲染宽度,动态写入--v-timeline-dot-sizeCSS 变量(VTimelineItem.tsx),供line-inset等依赖圆点尺寸的计算使用。
默认值传递:provideDefaults 机制
VTimeline上设置的dotColor、fillDot、hideOpposite、iconColor、lineInset、size、density、lineColor并非直接作用于根元素,而是通过provideDefaults统一注入到所有VTimelineItem/VTimelineDivider的默认 props 中(VTimeline.tsx)。这意味着:
- 在
v-timeline上设置的圆点样式会作用于全部条目; - 单个
v-timeline-item上显式传入的属性优先级更高,可做局部覆盖; - 这也是"组件的默认值下沉"模式在 Vuetify 中的典型应用,理解后对排查"为什么根属性没生效"很有帮助。
样式体系:Sass 变量与网格布局
时间轴的排布依赖 CSS Grid。以垂直方向为例,网格模板在 _variables.scss 中定义:
- 居中模式(
align="center"):minmax(auto, 50%) min-content minmax(auto, 50%),即"内容列 + 圆点列 + 对侧列"各占等宽空间($timeline-item-grid-template-center); - 自动模式:
auto min-content auto($timeline-item-grid-template-auto); - compact 密度下网格两端收窄为
0 min-content auto或auto min-content 0。
其他关键默认值包括:圆点默认直径$timeline-dot-size: 38px、条目内边距$timeline-item-padding: 24px、圆点圆角50%、线段颜色使用边框色变量rgba(var(--v-border-color), var(--v-border-opacity))、线宽读取--v-timeline-line-thickness(由根组件根据line-thickness写入,默认2px)。这些变量均以!default声明,可直接在应用层的 Vuetify 配置中覆盖,实现全局时间轴风格定制。
测试佐证
仓库还提供了浏览器级测试 VTimeline.spec.browser.tsx,覆盖方向、对齐、圆点渲染等核心行为,可作为验证组件 API 行为是否符合预期的参考。
相关组件与扩展阅读
时间轴常与以下组件组合使用,形成完整页面:
- VCard:作为时间轴条目的内容容器(文档中多数示例均以
v-card为主体); - VIcon:配合
icon属性或icon插槽填充圆点语义; - VGrid 布局系统:在时间轴外部组织多列页面结构。
组件完整的 API 文档可参考仓库内生成的 VTimeline.json 与 VTimelineItem.json。结合本文的源码解读,你可以在实际项目中自由组合direction、side、align、圆点样式与两个插槽,快速构建出符合业务需求、视觉一致的垂直或水平时间轴。
【免费下载链接】vuetify🐉 Vue Component Framework项目地址: https://gitcode.com/gh_mirrors/vu/vuetify
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考