先说一个反直觉的事实:DeepLearn 这个网站的代码,前后写了大概两周;但网站里的 15 章原创中文内容,我写了快一个月。
这大概注定了它是个内容型网站。所以这篇文章与其讲架构,不如讲我怎么让架构伺候内容。
它是什么
灵感来自 HKUDS/DeepTutor。这个项目的学习模式我觉得非常有效:路线图上打卡推进掌握度、每章配测验、抽认卡间隔复习。但它是英文的,而且需要本地跑环境,对刚入门的同学门槛不低。
我想把这套模式搬到一个打开浏览器就能用的中文网站里。最后做出来的东西有四条学习线——AI·LLM·Agent、前端开发、Python 编程、计算机基础——每章自带选择题测验和术语抽认卡,外加一个留言板。
为什么是"静态导出 + 一个函数"
架构的每个决定都在回答同一个问题:怎么让这个网站活得久、成本低、维护省力。
Next.js 开 output: 'export',整站导出成静态文件扔在 Vercel 的 CDN 上——这一层没有任何服务器,快,而且几乎不花钱。动态的部分(保存进度、判分、留言)全部收拢到一个文件里:api/index.js,一个挂在单个 Serverless 函数上的 Hono 应用,往下连 Neon 的 Postgres(免费档)。
整个后端就一个文件。有人说这样会越长越大,但我算过账:一个学习网站的服务端逻辑,撑死也就几张表的增删改查。真到了需要拆的那天再拆,总比为想象中的规模提前搭庙好。
还有个偷懒但好用的细节:API 收到第一次请求时自动 CREATE TABLE IF NOT EXISTS,部署完什么都不用跑,打开网站表就有了。
一个关键决定:内容写进 frontmatter
这是整个项目我最想保住的设计。
每章内容就是一个 Markdown 文件,测验和抽认卡直接写在 frontmatter 里:
---
title: 章节标题
order: 5
minutes: 8
quiz:
- question: 题干
options: [A, B, C, D]
answer: 1
explanation: 解析
flashcards:
- front: 术语
back: 解释
---
正文……构建时解析,路线图、课程页、测验、抽认卡自动收录。这意味着以后我加一章新课,只需要写一个 Markdown 文件——不用碰任何组件、路由或数据库脚本。
为什么要执着于此?因为我吃过反面案例的亏:很多教程网站的测验是硬编码在组件里的,写到第十篇就会开始复制粘贴 JSX,第十五篇就会想放弃。内容型网站的死亡方式通常不是架构崩了,而是更新内容的摩擦力大到没人想更新。frontmatter 这套把摩擦力压到了"写一篇博客"的水平。
进度同步,但不逼人注册
学习进度要跨设备保存才有意义,但注册是劝退利器。我的折中:给每个访客一个设备 ID,存在本地,进度、成绩、复习记录都挂在 ID 下同步进数据库。
代价很明显——换设备进度就断了。但对学习站来说这笔交易划算:用户可以零门槛先学起来,愿意留下的自然会回。
抽认卡沿用了间隔复习的思路,但做得很轻:翻卡时可以标「还不熟」,不熟的卡单独回炉。没有算法,没有艾宾浩斯曲线,就是个"不会的题放进错题本"的朴素机制。
写完之后
回头看,这个项目教我的事和 Arcade Hub 完全相反:游戏厅的难点在代码(40 款游戏、音效引擎),这里代码两周就收敛了,真正的工作量在内容——15 章东西,每一章都要自己先学明白才能写。AI 那条线我改了三稿,越写越发现很多"我以为我懂"的东西其实讲不清楚。
这也算这个网站给自己上的第一课。源码 MIT 开源,README 有一键部署指引(Vercel + Neon 全免费档),想搭一份自己的看这里。