✦ Puxiaoshuai · Time is a river painted on scrolls · Walk to the water’s end, sit and watch the clouds rise

TypeScript全栈Next.js2025.10.14 · 13 min read

个人博客(一):项目概览与技术选型

一个 Next.js 16 全栈博客的技术选型全景:为什么用 Hono 而不是 Server Actions、Prisma 7 的 driver adapter、数据访问层复用的思想。

L

Leo

2025.10.14 · Updated 2026.08.24

1 views
个人博客(一):项目概览与技术选型

一个「大道至简」的个人博客,前后台一体、双语言、支持 MDX 写作与 mermaid 图表。这一篇不写代码,先把地图铺开:项目是什么、用了什么技术、为什么这么选、目录怎么分层。

一、这是一个什么样的项目

一个全栈个人博客,前台公开、后台管理:

  • 前台(公开):首页、文章列表 / 详情、标签、拾语流(类似微博的时间线)、搜索、关于、双语言(/zh /en)。
  • 后台(管理):登录、仪表盘统计、文章 CRUD(含草稿与历史时间回填)、评论审核、拾语管理、标签管理、修改密码。
  • 数据存储:PostgreSQL,通过 Prisma 访问。

一条典型的流量路径:

二、技术栈全景与选型理由

技术选型理由(项目里的真实考虑)
框架Next.js 16 (App Router) + React 19 + TSApp Router 的 Server Component 让「页面直接在服务端查库渲染」,天然契合博客的 SSG / ISR 需求
样式Tailwind CSS v4纸感编辑风,utility 快速搭版;v4 用 @import "tailwindcss" 一行接入,无配置文件
数据库PostgreSQL生产与本地一致(Docker 也是 PG),全文搜索、聚合都是 PG 强项
ORMPrisma 7类型安全 + schema 即文档;7.x 新写法(driver adapter)适配 Node 直连,无 rust engine
APIHono轻量、类型好、中间件生态;在 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数据访问层复用
L

Leo

Blogger

Independent developer / Blogger and the maintainer of the original blog “大道至简”. Migrating years of posts and shiyu from WordPress to Next.js.

Reader comments

COMMENTS · 0

Leave a comment

Comments are shown after moderation · be kind0/100