面向哲思的编程与架构
笔记哲思阅读动态搜索RSS 订阅
切换到深色模式
搜索
RSS 订阅
切换到深色模式
© 2026 Vic Chen. All rights reserved.CC BY-NC-ND 4.0
← 笔记
个人发布平台的演进

个人发布平台的演进

2025年3月6日2,7588分钟

从 WordPress 到 Hexo、Hugo,再到用 Next.js 16 自研一套完整的个人发布平台——记录N年(N>20)折腾历程、每次迁移的真实动因,以及最终技术选型的完整逻辑。


目录
  • 1. 折腾史:从托管到自研
  • 1.1 WordPress
  • 1.2 Hexo
  • 1.3 Hugo
  • 1.4 为什么最终选择自研
  • 2. 技术选型
  • 2.1 整体架构
  • 2.2 Next.js 16 App Router
  • 2.3 MDX 渲染链
  • 2.4 Upstash Redis:无服务器友好的存储
  • 2.5 评论系统:从零自建
  • 2.6 受保护笔记:真加密而非遮挡
  • 2.7 样式:CSS 变量 + Tailwind 4
  • 2.8 部署:Vercel + Git 自动触发
  • 3. 有意思的细节
  • 4.还没做完的事
目录
  • 1. 折腾史:从托管到自研
  • 1.1 WordPress
  • 1.2 Hexo
  • 1.3 Hugo
  • 1.4 为什么最终选择自研
  • 2. 技术选型
  • 2.1 整体架构
  • 2.2 Next.js 16 App Router
  • 2.3 MDX 渲染链
  • 2.4 Upstash Redis:无服务器友好的存储
  • 2.5 评论系统:从零自建
  • 2.6 受保护笔记:真加密而非遮挡
  • 2.7 样式:CSS 变量 + Tailwind 4
  • 2.8 部署:Vercel + Git 自动触发
  • 3. 有意思的细节
  • 4.还没做完的事
目录
  1. 1. 折腾史:从托管到自研
  2. 1.1 WordPress
  3. 1.2 Hexo
  4. 1.3 Hugo
  5. 1.4 为什么最终选择自研
  6. 2. 技术选型
  7. 2.1 整体架构
  8. 2.2 Next.js 16 App Router
  9. 2.3 MDX 渲染链
  10. 2.4 Upstash Redis:无服务器友好的存储
  11. 2.5 评论系统:从零自建
  12. 2.6 受保护笔记:真加密而非遮挡
  13. 2.7 样式:CSS 变量 + Tailwind 4
  14. 2.8 部署:Vercel + Git 自动触发
  15. 3. 有意思的细节
  16. 4.还没做完的事
架构
相关文章
  • 01
    「相关文章」功能的设计与实现2026/05
  • 02
    Prefetch 的完整图景2026/07
  • 03
    用 View Transitions + Skeleton 消灭页面跳转的割裂感2026/07
← 上一篇高频交易系统核心构建技术的探索与实践(一)
下一篇 →期权估值(一):Black-Scholes 模型的红与黑

评论

© 2026 Vic Chen · 面向哲思的编程与架构CC BY-NC-ND 4.0

这篇文章早就该写了。

每次有人问起「你的网站是用什么搭的」、「你的文章是用什么软件写的」,我都要花五分钟解释一遍——不是因为答案复杂,而是因为现在这个状态是多年折腾的结果,不说前因后果讲不清楚。索性写成文章,下次直接发链接。

1. 折腾史:从托管到自研

1.1 WordPress

最早建站是用 WordPress(Chen 注:准确地说,是 MSN Space。),托管在 VPS 上。WordPress 的好处是开箱即用——主题商城、插件生态、所见即所得编辑器,适合快速起步。但用了多年之后,问题越来越明显:

  • 性能:PHP 动态渲染,每次请求都要查数据库。没有 CDN 的情况下,首屏速度很差。加了缓存插件有改善,但配置繁琐,也容易失效。

  • 维护成本:WordPress 的插件更新频率极高,安全漏洞补丁隔几周就来一次。数据库备份、版本升级、服务器续费,运维占用了大量精力,写作时间反而少了。

  • 写作体验:古腾堡编辑器在代码块、公式支持上很弱。装了一堆插件之后,写技术文章依然很别扭。我真正想要的是 Markdown,而不是块编辑器。

1.2 Hexo

后来尝试迁到了 Hexo。这次迁移很彻底——告别动态服务器,全部变成静态文件,推到 GitHub Pages。

用 Markdown 写文章这件事变得愉快多了。但 Hexo 的问题是 Node.js 生态太老,主题系统基于模板引擎(EJS / Nunjucks),扩展起来像在写模板代码,而不是写组件。我想加个自定义的交互功能,要么 jQuery 硬写,要么放弃。

另一个问题:全量构建。文章多了之后,每次 hexo generate 都要等两三分钟,改一个字也得全量重跑,开发体验很糟糕。

1.3 Hugo

为了解决构建速度,换到了 Hugo。Go 编译的静态站点生成器,构建速度确实飞快,千篇文章毫秒级。

但 Hugo 带来了新的问题:模板语言的表达力。Hugo 的模板系统是 Go template,逻辑稍微复杂一点就开始反人类。写一个需要遍历、过滤、聚合的功能,代码可读性极差。更重要的是,Hugo 本质上还是静态的——如果我想要评论、浏览量、互动反应,就必须依赖外部服务(Disqus、utterances 等),而这些服务要么有隐私问题,要么样式无法和站点融合,要么需要 GitHub 账号才能评论,门槛过高。

1.4 为什么最终选择自研

用了三个平台之后,我的需求变得很清晰:

  1. 用 Markdown / MDX(Chen 注:MDX 是 Markdown 的超集,允许在 .mdx 文件里直接使用 JSX 组件。写普通段落和代码块用 Markdown 语法,需要交互或自定义样式时嵌入 React 组件,两者可以自由混排。) 写作,本地文件管理,不依赖任何 CMS
  2. 静态生成 + 按需动态,构建快、性能好
  3. 评论系统自己掌控,不依赖第三方,数据在自己手里
  4. 代码高亮、数学公式、自定义组件,开箱即用
  5. 想加什么功能就加什么,不受主题和插件限制

没有一个现成方案能同时满足这五点。自研是必然选择。


2. 技术选型

2.1 整体架构

2.2 Next.js 16 App Router

选 Next.js 而不是 Astro、Remix 或纯静态生成器,核心原因有两个:

  1. 混合渲染。博客列表页、文章详情页可以静态生成(或 ISR(Chen 注:Incremental Static Regeneration,增量静态再生成。页面在构建时生成静态 HTML,但可以设置一个重新验证间隔(revalidate),到期后下次请求触发后台重新生成,无需全量重建。兼顾了静态页面的性能和动态内容的时效性。)),但评论 API、浏览量 API 需要动态响应。Next.js 在一个框架内把这两件事处理得很干净,不需要单独维护一个后端服务。

  2. React Server Components。文章页面的渲染链——读文件、序列化 MDX、加密受保护内容——全部在服务端完成,客户端拿到的是可以直接渲染的数据。需要交互的部分(评论区、表情反应、主题切换)单独拆成 Client Components。这个边界划分得很自然,符合「服务端做数据,客户端做交互」的直觉。

这个版本(16.x)还开了 experimental.viewTransition,页面跳转时用浏览器原生 View Transitions API 做过渡动画,几行配置,效果很好。

2.3 MDX 渲染链

写作格式是 MDX——Markdown 的超集,可以在文章里嵌入 React 组件。文章存在 content/notes/*.mdx,纯文本文件,Git 管理,不需要数据库。

渲染走 next-mdx-remote,服务端序列化后传给客户端渲染。序列化时挂了一条 rehype 插件链:

阶段插件作用
remarkremark-gfm表格、删除线等 GFM 扩展语法
remarkremark-math识别 $...$ 和 $$...$$ LaTeX 公式
rehyperehype-slug为标题自动添加锚点 id
rehyperehype-figure-caption将图片 alt 文字渲染为 <figcaption>
rehyperehype-katex将公式节点渲染为 KaTeX HTML
rehyperehype-mermaid提取 mermaid 代码块,替换为自定义组件
rehyperehype-pretty-codeShiki 语法高亮,支持 diff 标注
rehyperehype-code-lang在代码块右上角注入语言标签

代码高亮用 Shiki,主题是 github-light / github-dark,支持 diff 标注(自己写了个 Shiki transformer),高亮指定行。公式用 KaTeX,服务端渲染,不需要客户端 JS。

2.4 Upstash Redis:无服务器友好的存储

动态数据(评论、浏览量、Reaction 计数)全部存在 Upstash Redis。选它而不是传统 Redis 或关系型数据库,原因很简单:

  • Serverless 友好:HTTP 协议连接,不需要长连接,Vercel Functions 里用没有连接池问题
  • 按需付费:流量小的个人博客,免费套餐足够
  • 延迟低:全球多区域,配合 Vercel Edge 延迟可以接受

评论数据的结构设计:

Key类型用途
comment:{id}Hash单条评论的完整数据
comments:{slug}List某篇文章的评论 ID 列表
comments:__all__List全站评论 ID 列表(管理后台用)
comment_count:{slug}String已发布评论计数
views:note:{slug}String文章浏览量
stats:pvString全站 PV
stats:uvHyperLogLog全站 UV(去重访客估算)

UV 统计用 HyperLogLog(Chen 注:HyperLogLog 是一种概率数据结构,用极少的内存(固定 12KB)估算集合的基数(不重复元素数量)。Redis 原生支持,用 PFADD 写入、PFCOUNT 读取,误差率约 0.81%。相比存储完整 IP 列表,它既省空间又保护隐私——只能知道「大约有多少个不重复访客」,无法还原具体 IP。)(PFADD / PFCOUNT),不存真实 IP,误差率 0.81%,对个人博客足够准确。统计在 Middleware 里做,每次请求都会命中,包括静态页面。

2.5 评论系统:从零自建

这是自研最值的地方。系统功能:

  • 匿名评论(只填名字),无需注册
  • 嵌套回复(一层,不做多级)
  • OpenAI Moderation API 自动内容审核——免费,无额度限制,违规评论自动标记为 blocked
  • 管理后台可以查看、删除评论,重算计数

「为什么不用 Giscus / utterances?」

因为我不想让读者必须有 GitHub 账号才能评论。技术博客的读者不都是开发者,而且这类嵌入方案在样式上很难和站点融合。自建的代价是几百行代码,换来的是完全的掌控权,值得。

2.6 受保护笔记:真加密而非遮挡

有些笔记不想公开,但又想放在这里(比如涉及具体项目细节或尚未公开的分析)。早期的方案是 CSS 遮挡——页面里有内容,只是用样式盖住。这种方案形同虚设,开 DevTools 就能看。

现在的方案是真加密:

加密在构建阶段完成,构建产物里只有密文。密码来自服务端环境变量(不带 NEXT_PUBLIC_ 前缀,不会打进客户端 bundle)。解密完全在浏览器的 Web Crypto API 里运行,密码不经过服务器。

2.7 样式:CSS 变量 + Tailwind 4

样式方案是 CSS 变量定义设计 token,Tailwind 4 做工具类补充。明暗主题通过 [data-theme="dark"] 选择器切换 CSS 变量值,next-themes 负责读写 data-theme 属性并处理 SSR hydration 问题。

没有用 styled-components 或 CSS Modules,原因是:组件数量不多,内联样式 + CSS 变量已经够用,避免引入额外的运行时或构建复杂度。

2.8 部署:Vercel + Git 自动触发

推送到 main 分支,Vercel 自动构建部署。构建时注入的系统变量(VERCEL_GIT_COMMIT_SHA、VERCEL_ENV 等)用来在首页展示当前部署信息,包括 commit hash、构建时间、部署环境。

动态页面展示过去一年的 commit 热力图,数据来自 GitHub API,每小时通过 Next.js ISR 重新请求一次。


3. 有意思的细节

几个在技术上有点意思、但不容易在主流教程里看到的设计:

  • 批注系统。文章里可以用 <Revision> 和 <Follow> 组件标记修订批注和行动跟进。宽屏下批注浮在正文右侧,滚动时跟随对应段落,支持碰撞避让(批注密集时自动垂直错开)。窄屏降级为 tooltip。

  • pangu.js。中英文混排时自动在汉字和英文/数字之间加空格——这是排版规范,但手动打空格太烦。渲染后用 pangu 库在客户端处理一次,代价是一次轻微的 DOM 操作,换来的是所有文章都符合排版规范。

  • HoverPrefetchLink。鼠标悬停到文章链接时才触发 prefetch,而不是用 <Link> 的默认行为(进入视口即 prefetch)。文章列表页有几十个链接,全量 prefetch 对小流量博客来说是浪费;悬停时再 prefetch 是更合理的触发时机。

  • pull-to-refresh。移动端下拉刷新,用 touch 事件实现,有 CSS 动画反馈。很小的细节,但让移动端体验更接近原生 App。


4.还没做完的事

  • 搜索:目前没有全文搜索,靠分类和标签浏览(Chen 注:实现了轻量版搜索,支持标题、摘要与标签,而全文搜索高度依赖后端存储,故不实现。)。计划用 Pagefind(静态全文搜索)或者直接在 Upstash 存索引。
  • RSS:应该有,还没做。
  • OG 图片自动生成:目前所有文章共用一张默认 OG 图,计划用 @vercel/og 按文章标题动态生成。
  • 写作统计:字数、发布频率的可视化,想法有了,实现还没排期。

✓
建站本身是副产品。真正的目的是有个地方写东西,而且写起来不别扭。折腾了五年,现在的状态离「不别扭」最近。