个人博客(一):项目概览与技术选型
一个 Next.js 16 全栈博客的技术选型全景:为什么用 Hono 而不是 Server Actions、Prisma 7 的 driver adapter、数据访问层复用的思想。
Leo
2025.10.14 · Updated 2026.08.24
一个「大道至简」的个人博客,前后台一体、双语言、支持 MDX 写作与 mermaid 图表。这一篇不写代码,先把地图铺开:项目是什么、用了什么技术、为什么这么选、目录怎么分层。
一、这是一个什么样的项目
一个全栈个人博客,前台公开、后台管理:
- 前台(公开):首页、文章列表 / 详情、标签、拾语流(类似微博的时间线)、搜索、关于、双语言(
/zh/en)。 - 后台(管理):登录、仪表盘统计、文章 CRUD(含草稿与历史时间回填)、评论审核、拾语管理、标签管理、修改密码。
- 数据存储:PostgreSQL,通过 Prisma 访问。
一条典型的流量路径:
二、技术栈全景与选型理由
| 层 | 技术 | 选型理由(项目里的真实考虑) |
|---|---|---|
| 框架 | Next.js 16 (App Router) + React 19 + TS | App Router 的 Server Component 让「页面直接在服务端查库渲染」,天然契合博客的 SSG / ISR 需求 |
| 样式 | Tailwind CSS v4 | 纸感编辑风,utility 快速搭版;v4 用 @import "tailwindcss" 一行接入,无配置文件 |
| 数据库 | PostgreSQL | 生产与本地一致(Docker 也是 PG),全文搜索、聚合都是 PG 强项 |
| ORM | Prisma 7 | 类型安全 + schema 即文档;7.x 新写法(driver adapter)适配 Node 直连,无 rust engine |
| API | Hono | 轻量、类型好、中间件生态;在 Next 里只用一个 Route Handler 转发即可 |
| 认证 | NextAuth v4 | 凭证登录 + JWT session;callback 注入 role,实现 ADMIN 权限 |
| 国际化 | next-intl | 与 App Router 配合成熟,/zh /en 各自静态生成 |
| 内容 | MDX(next-mdx-remote + remark/rehype) | 博客正文直接写 Markdown,还能内嵌 React 组件(mermaid 图) |
| 图表 | mermaid | 文章里写 ```mermaid 代码块,客户端渲染成 SVG |
| 部署 | 宝塔 Nginx + PM2 | 国内单机服务器最常见方案;Docker 只跑本地数据库 |
三、三个「为什么」
1. 为什么不用 Server Actions,而是 Route Handler + Hono?
项目里评论、点赞、后台 CRUD 全是客户端 fetch('/api/...'):
- 写操作要按 IP 限流、要统一 JSON 响应,Hono 的中间件和
c.json()比 Server Actions 更顺手; - 后台管理是受控表单(fetch 提交),不需要 Server Action 的渐进增强;
- API 独立成层,以后可以单独暴露给其他端(小程序 / App)。
这俩不是互斥的:Server Actions 适合「表单即操作」的场景,API 路由适合「程序化接口」。
2. 为什么用 Prisma 7 的 driver adapter?
Prisma 7 移除了旧的 rust query engine,用 @prisma/adapter-pg 直连 PG,包更小、启动更快、部署更省心(不用下载二进制 engine)。
3. 为什么前台页面不直接读数据库、还要抽一层 lib/posts.ts?
页面(Server Component)和 Hono API 共用同一套查询函数,避免两处各写一遍查询逻辑(分页、置顶排序、统计都要一致)。这就是「数据访问层(DAL)」思想。
四、目录结构逐层拆解
blog-project/
├── app/ # 路由层(App Router)
│ ├── api/
│ │ ├── [[...route]]/route.ts # ★ 全 API 入口:转发给 Hono
│ │ └── auth/[...nextauth]/route.ts # NextAuth 专用路由
│ ├── robots.ts # 爬虫协议
│ ├── sitemap.ts # 站点地图(自动生成)
│ ├── not-found.tsx # 根级 404(无语言前缀时的兜底)
│ └── [locale]/ # ★ 动态段:/zh、/en
│ ├── layout.tsx # 根布局(字体、语言、metadata)
│ ├── (blog)/ # ★ 路由组:前台(页头/页脚/首页/文章/标签/拾语/关于)
│ ├── (admin)/ # ★ 路由组:后台(layout 服务端权限守卫 + 仪表盘/文章/评论/拾语/标签/设置)
│ ├── login/ # 登录页(独立于两套布局)
│ └── search/ # 搜索结果页
│
├── components/ # 组件层(common / posts / admin / shiyu / tags)
├── lib/ # ★ 业务逻辑层(非组件代码)
│ ├── hono/ # ★ Hono API:app + posts/shiyu/comments/search/account/ip/rateLimit
│ ├── generated/prisma/ # Prisma 生成类型(gitignore,服务器上重新 generate)
│ ├── auth.ts # NextAuth 配置 + requireAdmin 守卫
│ ├── db.ts # Prisma 单例
│ ├── posts.ts / shiyu.ts # 数据访问层(页面与 API 共用)
│ ├── admin.ts # 后台专用查询
│ ├── mdx.ts # MDX 编译 + TOC 提取 + mermaid 插件
│ ├── revalidate.ts # ISR 路径刷新封装(按语言逐个刷)
│ ├── site.ts # 站点对外 URL 常量(SITE_URL)
│ └── coverPresets.ts # 封面图预设池 + randomCover
├── i18n/ # next-intl:routing / request / navigation
├── messages/ # zh.json / en.json 文案字典
├── prisma/ # schema.prisma + migrations + seed.ts
├── proxy.ts # ★ Next 16 的 middleware:next-auth + next-intl 组合
└── next.config.ts # images 域名白名单 + LocatorJS + next-intl 插件
两个关键路由约定
动态段 [locale]:app/[locale]/ 让 /zh、/en 共用同一套页面代码,params.locale 决定加载哪份文案。根布局 generateStaticParams() 返回 ['zh','en'],于是每个语言各自静态生成一份 HTML。
路由组 (blog) / (admin):圆括号不会进 URL,只用来共享布局。(admin)/layout.tsx 做权限守卫:进后台前服务端校验 session,非 ADMIN 直接 redirect('/login')。login/ 放在两组之外,不套任何布局。
lib/ 的分层哲学
页面(Server Component) Hono API
│ │
└────────► lib/posts.ts ◄───┘ ← 数据访问层,两边共用
│
Prisma (lib/db.ts)
│
PostgreSQL
lib/hono/ 是 API 专属逻辑(路由、限流、IP、权限),只被 app/api 调用;lib/*.ts 数据访问层页面与 API 共用,保证前后台看到的排序、分页规则一致。
五、数据模型一览(先有个印象)
Post ← 文章(title/slug/content MDX/excerpt/cover/published)
└─ views / likes 阅读量 / 点赞(increment 原子自增)
└─ readingMinutes 阅读时长(发布时算好入库,列表不拉正文)
Tag ← 标签(name unique + slug unique,多对多)
User ← 用户(email unique + username + password[bcrypt] + role[ADMIN/USER])
Comment ← 评论(published 审核位 + ip 记录,按 ip 限流)
Shiyu ← 拾语(一句话 + 配图 + no 流水号 + pinned 置顶)
一个性能优化点:readingMinutes 在发布时就算好写进数据库,列表页只 select 它而不拉 MDX 正文——正文可能几万字,列表拉它就是灾难。
六、本节小结
- 这是一个 Next.js 16 全栈博客:前台 SSG/ISR 读库渲染,后台 + 写操作走 Hono API。
- 目录分三层:
app/(路由)、components/(UI)、lib/(业务逻辑)。 - 三个核心决策:Server Actions vs API 路由、Prisma driver adapter、数据访问层复用。
Leo
BloggerIndependent developer / Blogger and the maintainer of the original blog “大道至简”. Migrating years of posts and shiyu from WordPress to Next.js.
Reader comments
COMMENTS · 0Leave a comment