本网站最初只有首页、项目和关于页面。它适合展示已经完成的作品,却不适合持续记录项目决策、开发过程和长篇内容。
为了展示文档/个人动态,我在26年8月13日正式为网站增加了文档区域:文章继续以普通 Markdown 文件保存,网页负责把它们解析成与主站一致的阅读界面。
最初要解决的问题
静态站点生成器很适合文档,但如果把文稿和网站构建强绑定,每次改一个错别字都要重新构建、上传整站。对于会频繁更新的日志和项目说明,这个反馈周期太长。
最终确定了四个约束:
- 内容使用标准 Markdown,不依赖专用编辑器;
- 文稿变化不触发 Next.js 重新编译;
- 文档视觉继续沿用主站的导航、字体、颜色和间距;
- 生产环境可以把内容放进独立 Git 仓库,程序与内容分别发布。
为什么没有直接使用现成方案
在构建文档功能时,我也调研了一下是否有适合我使用的现成方案,发现 VitePress 的默认主题、目录、搜索和代码块体验都很成熟,很适合独立技术站点。
但 VitePress 是由 Vite + Vue 驱动的,而本个人站点是使用 Next.js 构建的。如果单独部署 VitePress,就需要维护两套构建、主题和路由边界。
因此我以 VitePress 的阅读体验作为视觉参考,用 Next.js App Router 实现内容层。这样文档和主站共享同一套组件,同时仍能在运行时读取 Markdown。
从文件到页面
现在访问一篇文档时,服务端会完成以下步骤:
- 从
CAVIARLAB_CONTENT_DIR指向的目录中查找 Markdown; - 解析 Front Matter,得到标题、简介、日期和文档种类;
- 提取二至四级标题,生成右侧“在本页”目录;
- 使用 GFM 和 Shiki 渲染表格、任务列表与代码高亮;
- 把相对文档链接转换为站内路由,把相对图片转换为受限资源路由;
- 根据文件修改时间和大小复用缓存,避免没有变化时重复解析。
完整的模块边界和部署方式记录在动态文档系统架构中。
内容如何更新
在开发环境中,保存 Markdown 后刷新页面,索引缓存最迟约一秒重新扫描文件。单篇文档只有在 mtime 或文件大小变化时才会重新解析。
生产环境中,网站程序与内容仓库分开。同步脚本执行 git pull --ff-only,然后调用带 Bearer Secret 的刷新接口清理进程内缓存。修改文章不需要重新构建 Next.js;只有调整组件、解析器或样式时才重新发布程序。
目前的边界
当前系统专注于“文件即内容”,暂时没有全文搜索和图片处理流水线。Markdown 图片也没有天然携带宽高,因此正文仍使用原生 <img>,图片性能主要依靠导入前压缩和尺寸控制。
下一步将补充全文搜索、内容仓库自动同步,以及在导入阶段自动生成多尺寸图片。文档系统已经能够稳定承载内容,后续功能应该继续服务于阅读本身。