-rw-r--r--2026-08-022 min577 words

用 Nuxt 3 搭建一个按篇付费的博客

很多人问这个站是怎么搭起来的。这篇文章把整体架构摊开讲清楚:渲染策略、内容管线、付费墙安全模型三个核心问题。

先想清楚:付费内容放在哪里

按篇付费的博客,最容易踩的坑是把全文塞进前端 payload 再用 CSS 遮罩。右键查看源码就能白嫖,这不行。所以第一条铁律:

付费内容绝不出现在公开的 HTTP 响应里,包括 SSR 的 HTML、水合 payload、以及任何内部 API。

本站的解法是「构建期物理隔离」:源文件 articles/*.md 同时生成两份产物——

server/assets/content/
├── manifest.json          # 公开索引
├── previews/<slug>.json   # 预览段 HTML(可被爬虫抓取,SEO 友好)
└── full/<slug>.json       # 完整 HTML(仅 entitlement API 读取)

Nitro 把 server/assets 挂载为服务端私有存储,公开 API 只读 previews/,全文只通过一条校验购买状态的接口下发。物理上不可达,而不是逻辑上隐藏。

渲染策略:SSR + 客户端解锁

页面本体走 SSR 并配合 swr 缓存,因为公开部分与用户无关;购买状态则在水合后由客户端请求 /api/articles/:slug/full 拉取。这样同一篇文章的页面可以复用缓存,付费校验只发生在数据接口上。

用一张图描述整条链路:

内容管线:markdown 是唯一事实源

写作工作流保持极简——本地写 markdown、push 到 Git 仓库,没有任何在线后台。构建脚本用 unified 生态处理渲染:

const processor = unified()
  .use(remarkParse)
  .use(remarkGfm)        // 表格 / 脚注 / 任务列表
  .use(remarkMath)       // 数学公式入口
  .use(remarkRehype, { allowDangerousHtml: true })
  .use(rehypeKatex)      // 渲染 KaTeX
  .use(rehypeSlug)       // 标题锚点,供目录使用

代码高亮用的是 Shiki 的 vesper 主题,与整站暗色美学一致;Mermaid 图表在构建期仅留下占位符,浏览器端再实例化渲染,避免把整个图表引擎塞进首屏包。

数学公式长什么样

讨论缓存命中率时随手推导一下 Little's Law1

L=λWL = \lambda W

其中 LL 是系统中平均请求数,λ\lambda 是到达率,WW 是平均停留时间。它对容量规划的指导意义在于:并发数 = 吞吐 × 延迟,三个量知二求一。

小结

  • 付费墙的安全靠「构建期隔离 + 服务端校验」,不靠前端遮罩
  • 渲染走 SSR,付费解锁走客户端接口,缓存互不干扰
  • 内容管线纯 Node 脚本,写作就是写 markdown

这套架构的数据库层(订单、购买凭证、RLS 策略)在后续的付费专栏里展开。

Footnotes

  1. Little, J. D. C. (1961). "A Proof for the Queuing Formula".

评论

# 评论系统未配置:部署 Waline 后设置 NUXT_PUBLIC_WALINE_SERVER_URL 即可启用
NORMALdocklog@prod