VitePress 文章元信息组件
组件简介
这个组件用于在每篇文章正文的最上方,统一展示一行文章元信息,包括创建时间、最后更新时间和相对时间。它的目标是让读者在打开一篇文章时,能第一时间知道这篇内容的"新鲜度",而不必依赖路由记忆或文件名约定。
对于个人博客来说,文章发表时间是一个隐性的信任信号。一篇没有日期的技术文章,读者很难判断它是否还跟得上当前生态;而把时间显式标注在标题正下方,可以让阅读体验更接近成熟的内容站。
核心功能
- 在文章 H1 标题下方展示一条紧凑的元信息卡片
- 默认通过
git log自动解析每篇文章的首次提交时间作为创建时间 - 同步展示最后一次提交时间,便于读者识别文章是否近期修订过
- 客户端补齐相对时间(如"3 天前""2 个月前"),让时间感受更直观
- 当创建和最后更新发生在同一天时自动隐藏更新时间,避免冗余
- 仅在
posts/目录下的文章中渲染,首页、404 等特殊页面不出现 - 支持文章作者通过 frontmatter 手动覆盖时间,方便迁移历史内容
- 提供独立的 CSS 变量,便于后续微调配色和间距
使用方式
这个组件的源码位于 .vitepress/theme/components/ArticleMeta.vue,并通过以下三个入口接入:
.vitepress/config.mts.vitepress/theme/index.ts.vitepress/theme/Layout.vue
接入方式延续了项目已有的主题扩展结构:
- 在站点配置中注册
transformPageData钩子,在构建/启动期为每篇文章注入时间字段 - 在主题入口引入元信息对应的全局样式变量文件
- 在自定义
Layout中通过doc-before插槽挂载组件
接入完成后,posts/ 目录下的所有文章都会自动获得这一行信息,普通 Markdown 文章不需要手动写任何额外内容。
数据来源策略
文章时间的解析采用三层兜底,按优先级从高到低依次尝试:
- frontmatter 手动声明:如果文章头部写了
date: 2024-12-01,组件会优先使用这个值 - git 首次提交时间:通过
git log --diff-filter=A --follow解析文件被加入仓库的那一次提交时间 - 文件系统时间:当 git 历史不可用时,回退到文件的
birthtime或mtime
最后更新时间的来源相对简单,统一取 git log -1 的最近一次提交时间。
这样设计的好处是:
- 已经发布的存量文章不需要逐个补 frontmatter,git 自动兜底
- 历史文章迁移或希望对外公布特定日期时,作者可以通过 frontmatter 显式覆盖
- 即便 git 不可用(比如本地开发刚 clone 的浅仓库),也不会让组件直接报错
实现思路
整体实现分为构建期和运行期两部分。
构建期由 VitePress 的 transformPageData 钩子负责,它会在生成每个页面的元数据时被调用。我在钩子里执行 git log 命令获取时间戳,并把结果写到 pageData.frontmatter 上。为了避免开发模式下每次保存都重新执行命令,钩子里维护了一份内存缓存,只在第一次解析某个文件时真正调用 git。
运行期由 ArticleMeta.vue 负责。它通过 useData() 拿到当前页面的 frontmatter,按优先级解析出创建时间和更新时间,再交给模板渲染。相对时间在客户端挂载完成后才计算,避免和服务端预渲染的内容产生 hydration 差异。
主题接入方式
该组件是通过 VitePress 默认主题扩展 的方式接入的,而不是直接写在某一篇 Markdown 中。
接入方式的关键点有三个:
- 在
.vitepress/config.mts中注册transformPageData钩子,集中处理时间字段 - 在
.vitepress/theme/index.ts中继承默认主题并引入样式 - 在自定义
Layout中通过doc-before插槽注入组件
这种做法的优点和置顶组件、加载进度条一致:
- 不需要修改任何文章 Markdown
- 能做到全站文章统一生效
- 和默认主题耦合较低,未来 VitePress 升级也好维护
部署侧注意事项
由于组件依赖 git log 解析首次提交时间,CI 环境必须拉取完整 git 历史,否则所有文章都会显示同一个部署日期。
当前项目的 GitHub Actions 工作流已经在 actions/checkout@v4 中显式声明了:
- uses: actions/checkout@v4
with:
fetch-depth: 0fetch-depth: 0 的含义是"拉取完整提交历史",默认值是 1(浅克隆),后者会让 git log 拿不到旧提交。如果以后切换到其他 CI 平台或部署方式,也需要保留这个设置。
手动覆盖时间
任意文章都可以通过 frontmatter 显式声明 date 字段来覆盖 git 自动解析的时间:
---
date: 2024-12-01
---
# 文章标题
正文内容...这个能力主要用于以下场景:
- 历史文章从其他平台迁移过来,希望保留原始发布时间
- 文章经过大幅重写,希望把"创建时间"对外重置为当前日期
- 草稿先入库、过段时间再正式发布,希望显示真正的发布日期
如果不写 date 字段,组件就会自动回退到 git 解析结果,无需任何额外配置。
样式与可配置项
当前组件对外暴露了以下主题级 CSS 变量,定义在 .vitepress/theme/styles/article-meta.css:
--blog-article-meta-color--blog-article-meta-color-soft--blog-article-meta-icon-color--blog-article-meta-bg--blog-article-meta-border--blog-article-meta-radius--blog-article-meta-gap
默认视觉方向延续了置顶组件那套浅青加浅蓝的清新风格,配合圆角、轻边框和淡渐变背景,在标题和正文之间形成一个轻量的视觉过渡。整体特点如下:
- 紧贴 H1 标题下方,但保留一定外边距避免压迫感
- 使用
tabular-nums让日期字符在不同字符宽度下对齐更稳 - 暗色主题下颜色和边框透明度独立配置,避免和深色背景割裂
- 移动端自动收紧间距和字号,让窄屏下也能在一行内放下
适用场景
这个组件比较适合以下场景:
- 内容更新有节奏的个人博客
- 技术博客中需要让读者快速判断内容时效性的场景
- 希望在不引入第三方评论或社交插件的前提下,强化文章信息密度的项目
如果站点更接近静态文档站(内容偏稳定、不强调时间),这类组件的价值就会相对有限,可以考虑只显示创建时间或直接关闭。
当前限制
- 仅识别
posts/目录下的 Markdown 文章,其他位置的页面默认不渲染 - 相对时间是按客户端当前时刻计算的,刷新页面才会更新数值
- 不展示作者头像、标签、分类等元信息,这部分能力未做扩展
后续优化方向
如果后面还想继续完善,可以考虑这些方向:
- 增加可选的"标签"和"分类"展示能力,让元信息更丰富
- 提供 frontmatter 开关,允许个别文章显式隐藏元信息
- 在文章列表页(如分类首页)也复用这套时间解析逻辑
- 引入更精细的相对时间策略,例如超过一年后退化为"YYYY 年"
总结
文章元信息是一个体量不大、但能显著提升博客专业感的组件。它通过 transformPageData 钩子和主题插槽实现全站接入,既保持了 VitePress 默认主题的结构,又把"时间"这条隐性信号显式呈现给读者,是博客项目中性价比很高的一个增量能力。