← 返回文档
日志 ·

为个人网站建立可热更新的 Markdown 文档系统

记录 Caviar Lab 文档功能从技术选型、视觉收敛到正式内容导入的完整过程。

本网站最初只有首页、项目和关于页面。它适合展示已经完成的作品,却不适合持续记录项目决策、开发过程和长篇内容。

为了展示文档/个人动态,我在26年8月13日正式为网站增加了文档区域:文章继续以普通 Markdown 文件保存,网页负责把它们解析成与主站一致的阅读界面。

最初要解决的问题

静态站点生成器很适合文档,但如果把文稿和网站构建强绑定,每次改一个错别字都要重新构建、上传整站。对于会频繁更新的日志和项目说明,这个反馈周期太长。

最终确定了四个约束:

  1. 内容使用标准 Markdown,不依赖专用编辑器;
  2. 文稿变化不触发 Next.js 重新编译;
  3. 文档视觉继续沿用主站的导航、字体、颜色和间距;
  4. 生产环境可以把内容放进独立 Git 仓库,程序与内容分别发布。

为什么没有直接使用现成方案

在构建文档功能时,我也调研了一下是否有适合我使用的现成方案,发现 VitePress 的默认主题、目录、搜索和代码块体验都很成熟,很适合独立技术站点。
但 VitePress 是由 Vite + Vue 驱动的,而本个人站点是使用 Next.js 构建的。如果单独部署 VitePress,就需要维护两套构建、主题和路由边界。

因此我以 VitePress 的阅读体验作为视觉参考,用 Next.js App Router 实现内容层。这样文档和主站共享同一套组件,同时仍能在运行时读取 Markdown。

从文件到页面

现在访问一篇文档时,服务端会完成以下步骤:

  1. CAVIARLAB_CONTENT_DIR 指向的目录中查找 Markdown;
  2. 解析 Front Matter,得到标题、简介、日期和文档种类;
  3. 提取二至四级标题,生成右侧“在本页”目录;
  4. 使用 GFM 和 Shiki 渲染表格、任务列表与代码高亮;
  5. 把相对文档链接转换为站内路由,把相对图片转换为受限资源路由;
  6. 根据文件修改时间和大小复用缓存,避免没有变化时重复解析。

完整的模块边界和部署方式记录在动态文档系统架构中。

内容如何更新

在开发环境中,保存 Markdown 后刷新页面,索引缓存最迟约一秒重新扫描文件。单篇文档只有在 mtime 或文件大小变化时才会重新解析。

生产环境中,网站程序与内容仓库分开。同步脚本执行 git pull --ff-only,然后调用带 Bearer Secret 的刷新接口清理进程内缓存。修改文章不需要重新构建 Next.js;只有调整组件、解析器或样式时才重新发布程序。

目前的边界

当前系统专注于“文件即内容”,暂时没有全文搜索和图片处理流水线。Markdown 图片也没有天然携带宽高,因此正文仍使用原生 <img>,图片性能主要依靠导入前压缩和尺寸控制。

下一步将补充全文搜索、内容仓库自动同步,以及在导入阶段自动生成多尺寸图片。文档系统已经能够稳定承载内容,后续功能应该继续服务于阅读本身。