4.1 Next.js 生态系统


4.1 Next.js 生态系统

本节摘要:Next.js 是框架,但不是全部——数据层、状态管理、UI 组件、表单、验证都需要生态配合。本节给出 Next.js 生态的选型指南:ORM(Prisma)、状态管理(Zustand/React Query)、组件库(shadcn/ui)、表单与验证,帮助你在真实项目中快速选对工具。

读前必看(上)

阅读完本节,你应当能够:

  1. 用 Prisma 定义模型并操作数据库
  2. 理解状态管理的取舍(客户端状态 vs 服务器状态)
  3. 用 shadcn/ui 快速搭建 UI
  4. 用 React Hook Form + Zod 处理表单
  5. 建立"按需选型、最小引入"的生态观

问题与直觉:框架解决了"骨架",生态解决"器官"

Next.js 解决路由、渲染、部署——但"数据怎么存、状态怎么管、按钮长什么样"需要生态。选型的核心原则:先有需求,再选工具;能少用一个,就少用一个

直觉类比:Next.js 是"房子的框架",生态是"家具家电"——床(ORM)必须买,但"要不要投影仪(复杂状态库)"取决于你的需求。别为了装修而装修

💡 关键直觉:服务器组件改变了状态管理的需求——很多"状态"其实不需要客户端状态库:数据在服务器组件里获取,交互状态用 useState 局部管理即可。只有"全局共享 + 跨组件"的状态才需要库。

核心选型:五大类

2.1 ORM:Prisma

npm install prisma @prisma/client npx prisma init
// prisma/schema.prisma model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) createdAt DateTime @default(now()) }
// lib/db.ts import { PrismaClient } from "@prisma/client"; const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }; export const prisma = globalForPrisma.prisma ?? new PrismaClient(); if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
// 服务器组件中直接使用 const posts = await prisma.post.findMany({ where: { published: true }, orderBy: { createdAt: "desc" }, });

Prisma 的优势:类型安全(生成的客户端类型全推断)、迁移工具(prisma migrate)、可视化 Studio。

2.2 服务器状态:TanStack Query

需要"客户端取数 + 缓存 + 重试 + 失效"时(如无限滚动、实时刷新):

"use client"; import { useQuery } from "@tanstack/react-query"; export function Stats() { const { data, isLoading } = useQuery({ queryKey: ["stats"], queryFn: () => fetch("/api/stats").then((r) => r.json()), }); if (isLoading) return <div>加载中...</div>; return <div>{data?.value}</div>; }

取舍服务器组件能取数就别用 Query;只有"客户端需要的实时/交互数据"才引入。

2.3 客户端全局状态:Zustand

跨组件共享的 UI 状态(购物车、主题、用户偏好):

// store/cart.ts import { create } from "zustand"; export const useCart = create((set) => ({ items: [], add: (item) => set((s) => ({ items: [...s.items, item] })), })); // 组件中使用 const { items, add } = useCart();

取舍:只有"多组件共享且频繁变化"的状态需要全局库;组件内状态用 useState 即可。

2.4 UI 组件库:shadcn/ui

npx shadcn@latest init npx shadcn@latest add button card dialog
import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; export default function ProfileCard() { return ( <Card> <h2>用户资料</h2> <Button>编辑</Button> </Card> ); }

shadcn/ui 的优势:代码复制进项目(完全可控)、基于 Tailwind、与 Next.js 配合好。备选:Radix UI(无样式原语)、MUI/Ant Design(企业级组件)。

2.5 表单与验证:React Hook Form + Zod

npm install react-hook-form zod @hookform/resolvers
"use client"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; const schema = z.object({ email: z.string().email("邮箱格式不对"), password: z.string().min(8, "密码至少 8 位"), }); export function RegisterForm() { const { register, handleSubmit, formState: { errors } } = useForm({ resolver: zodResolver(schema), }); return ( <form onSubmit={handleSubmit((d) => console.log(d))}> <input {...register("email")} placeholder="邮箱" /> {errors.email && <p>{errors.email.message}</p>} <input type="password" {...register("password")} placeholder="密码" /> {errors.password && <p>{errors.password.message}</p>} <button>注册</button> </form> ); }

选型速查表

需求 推荐 何时可不用
数据库 Prisma 简单场景用 SQLite 裸查询
客户端取数 TanStack Query 服务器组件能取数
全局状态 Zustand 状态不跨组件
UI 组件 shadcn/ui 简单项目手写
表单验证 RHF + Zod 简单表单用 Server Actions 校验

工程实践要点:生态的最小引入原则

  1. 每次引入前问"真的需要吗":每多一个依赖,多一份升级风险与维护成本;
  2. 服务器优先:能用服务器组件/Server Action 解决的,不引客户端库;
  3. 类型安全优先:ORM(Prisma)与验证(Zod)带来类型安全,值得引入;
  4. 团队一致性:选型后写进文档,避免"一个项目三种写法";
  5. 关注维护状态:选活跃维护的库,避免长期不更新的"死库"。

常见误区与排查

误区 现象 正解
什么都引库 依赖爆炸、体积大 最小引入原则
服务器组件能取数还引 Query 双重请求 服务器组件优先
全局状态滥用 状态难追踪 局部 useState,全局才用库
Prisma 未初始化 连接报错 prisma init + migrate + generate
表单验证重复实现 校验散落 Zod schema 统一

动手演练:组装一个"标准技术栈"

# 初始化 Next.js + 生态 npx create-next-app@latest my-app cd my-app npm install prisma @prisma/client npx prisma init npx shadcn@latest init npm install zustand react-hook-form zod @hookform/resolvers
// 1. 定义数据模型 prisma/schema.prisma model Post { id Int @id @default(autoincrement()) title String content String? } // 2. 服务器组件取数 export default async function Home() { const posts = await prisma.post.findMany(); return posts.map((p) => <p key={p.id}>{p.title}</p>); } // 3. UI 组件 import { Button } from "@/components/ui/button";

标准技术栈成型:Prisma(数据)+ shadcn/ui(UI)+ RHF/Zod(表单)+ Zustand(全局状态,按需)——每个工具解决一类明确问题,组合成可维护的全栈项目

本节速览

  • ORM:Prisma 类型安全、迁移方便,服务器组件直接使用。
  • 客户端取数:TanStack Query,仅在服务器组件不够时引入。
  • 全局状态:Zustand,跨组件共享才用。
  • UI 组件:shadcn/ui,代码可控、与 Tailwind 配合好。
  • 表单:React Hook Form + Zod,声明式校验。
  • 最小引入:先问"真的需要吗",能服务器解决不引客户端库。

深入理解:技术选型的决策框架

生态选型决策

选型的四个维度

维度 考察点
必要性 框架能解决吗?多一个依赖多一分风险
活跃度 GitHub 近期提交、Issue 响应、版本更新
生态 文档质量、社区教程、周边工具
成本 学习成本、维护成本、迁移成本

常见误区

  • 追新:刚发布的库 Bug 多、生态薄——用"稳定 + 活跃维护"的版本;
  • 大而全:全家桶框架(如引整个 UI 框架只为用一个按钮)——按需引入;
  • 重复造轮子:成熟场景(认证、ORM)用成熟库,别自制。

一句话:选型的本质是"用最小依赖解决当前明确需求"——每个依赖都是长期的维护承诺,引入前三思。

常见问题速答

问:Prisma 和直接写 SQL 怎么选?
Prisma 提供类型安全、迁移、可视化,适合大多数应用;直接 SQL 适合复杂查询/性能极致的场景。默认 Prisma,遇到瓶颈再考虑裸 SQL

问:状态管理到底需不需要 Zustand?
服务器组件时代,多数"状态"不需要全局库:数据在服务器组件取、交互状态用 useState。只有"跨组件共享 + 频繁变化"(购物车、全局 UI)才需要 Zustand。

问:shadcn/ui 和其他组件库怎么选?
shadcn/ui 是"复制进项目"的(完全可控、无隐藏依赖),适合定制需求;Ant Design/MUI 是企业级组件库(装好即用),适合后台系统。按团队习惯选。

问:表单校验用服务端还是客户端?
两者都要:客户端(RHF + Zod)提供即时反馈,服务端(Action 内校验)是安全底线。永远信任服务端校验,客户端校验只是体验优化。

一句话总结

生态选型的核心是"最小依赖解决明确需求"——Prisma 管数据、shadcn/ui 管 UI、RHF+Zod 管表单、Zustand 管全局状态(按需)、TanStack Query 管客户端取数(按需)。每次引入前问"真的需要吗",能服务器解决不引客户端库。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U