在做内容型产品(课程、文档、博客)时,通常会遇到几个核心问题:
- 内容放哪里?数据库还是文件?
- 如何兼顾 SEO 和权限控制?
- 内容更新是否需要重新部署?
- 如何避免客户端直接暴露数据源?
这篇文章分享一种我们在实际项目中使用的方案:
基于 GitHub + Next.js + MDX,实现 SSG 预览 + 登录后加载完整内容
核心思路
整体设计可以拆成四层:
- GitHub:内容存储(私有仓库)
- 服务端:内容读取、缓存与权限控制
- SSG 页面:预渲染部分内容
- 客户端:登录后加载完整内容
1. 内容来源:GitHub API
内容统一存放在 GitHub 私有仓库(MDX 格式),通过 API 获取,而不是在 build 阶段下载到本地。
这样带来的好处:
- 内容更新不需要重新部署
- 支持 Git 工作流(PR、版本管理)
- 内容仓库可以独立维护
底层通过一个 loadMdx 方法,从 GitHub 拉取 MDX 原始内容。
2. 统一内容入口(服务层)
为了避免页面、API、缓存各自直接访问 GitHub,需要抽一层统一内容服务,负责:
- 组合课程元数据与正文内容
- 提供统一的内容访问入口
- 在这一层实现缓存策略
后续 SSG 页面刷新 和 内容 API 全部依赖这一层。
3. SSG 渲染:只输出预览内容
页面在构建阶段通过 SSG 渲染,只输出“部分内容”,用于:
- 首屏展示
- SEO 收录
这样可以在保证性能的同时,为后续的权限控制预留空间。
4. 登录后加载完整内容
完整内容不会在 SSG 阶段输出。用户登录后,通过客户端请求内部 API:
- 服务端校验登录态
- 调用内容服务(命中缓存)
- 返回完整 MDX 内容
整个过程不会直接访问 GitHub API,从而避免数据源暴露,并保证权限控制在服务端完成。
5. 缓存与内容更新
内容读取统一走服务层缓存,避免每次请求直接访问 GitHub。
当内容更新时,通过 Webhook 主动触发刷新:
- GitHub 仓库 push
- 触发 Webhook
- 调用服务端
/content-revalidate - 执行:
revalidateTag:刷新服务层缓存revalidatePath:刷新对应的 SSG 页面
这样可以在不重新部署的情况下,更新页面内容。
6. 方案的限制
需要注意:
revalidate只能更新已有页面,不能生成新的 SSG 页面
也就是说:
- 已有内容 → 可以更新
- 新增页面 → 不会自动参与 SSG
如果希望新页面也使用 SSG,仍然需要重新部署。
推荐策略
在实际项目中,可以这样组合:
- 内容更新 → Webhook +
revalidate - 新增路由 → 自动触发部署
这样可以在更新效率与系统稳定性之间取得平衡。
总结
这套方案本质上是:
SSG + 内容 API + 缓存 + Webhook 的组合
它解决了几个关键问题:
- 内容更新不依赖重新部署
- 保留 SSG 的 SEO 与性能优势
- 支持登录后的内容权限控制
- 避免客户端直接暴露数据源
如果你对具体实现细节(如内容加载、缓存策略、Webhook 处理等)感兴趣,可以直接查看我们的项目:https://github.com/raytonx-labs/raytonx-learn