ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Next.js全栈开发实战:从App Router到部署优化

Next.js全栈开发实战:从App Router到部署优化 直接说结论如果你的技术栈是React又想一个人扛起一个完整项目Next.js是目前最值得投入时间的框架没有之一。过去两年我用它做了三四个完整项目从内容站到SaaS后台都有最大的感受就是——以前前后端分离要折腾两套工程、两套部署、两套鉴权现在一套代码全搞定。这篇文章不是官方文档的翻译也不是课程广告而是我基于真实项目踩坑后的经验梳理覆盖从项目初始化、App Router核心概念、数据层设计、认证安全到部署优化的完整链路适合刚入门Next.js的开发者也适合那些已经写过几个页面但还没系统理解全栈能力的同学。1. 为什么选Next.js做全栈一个项目背后的真实考量1.1 全栈开发的痛点与Next.js的解题思路传统全栈开发是什么状态前端一个React应用跑在某个静态托管上后端一个Express或NestJS服务跑在服务器上中间还要处理CORS、两套鉴权逻辑、两份环境变量、两套部署流水线。看起来不算难但真正维护起来全是琐碎事改一个字段要同步改前后端类型定义调一次接口要看Network面板排查跨域部署时还要盯着两个平台的状态。我见过不少团队光是在“前后端联调”这件事上就能消耗掉三分之一的项目周期。Next.js的解题思路很直接把前端页面和后端逻辑放进同一个工程、同一个运行时。页面组件就是你的视图层Route Handlers或Server Actions就是你的接口层两者共享类型定义、共享工具函数、共享鉴权上下文。开发时一个npm run dev全部搞定部署时一个平台全部托管。这不是什么新概念但对于个人开发者和小团队来说它把“全栈”的门槛从一个系统工程降级成了一个框架的学习成本。另一个容易忽略的点是Next.js的渲染模型对全栈开发非常友好。你可以让一个页面在服务端直接查询数据库、拼装HTML返回给浏览器也可以把某些交互密集的模块标记为客户端渲染。同一个应用里SEO敏感页面走服务端渲染后台管理页面走客户端渲染互不干扰。这在传统前后端分离方案里要实现同样效果需要额外引入SSR框架或BFF层工程量完全不是一个量级。1.2 立项时的技术选型与目录规划我的建议很明确新项目直接用App Router别再学Pages Router了。App Router从Next.js 13引入到14、15版本已经非常成熟官方也在全力推进它成为唯一路由方案。虽然网上还有大量旧教程在讲pages/api那套写法但你现在新学的话直接上手App Router才是正确路径省得学完就面临迁移。初始化命令是官方脚手架npx create-next-applatest my-fullstack-app交互式选项里我会这样选TypeScript选YesESLint选YesTailwind CSS看个人偏好我建议选YesNext.js对Tailwind的支持做得很好src/目录选YesApp Router选Yesimport别名保持默认/*。这套选择下来你得到的是一个类型安全、代码规范、目录清晰的起点。如果你创建的是15版本的工程还会额外问是否启用Turbopack。我目前建议选Yes开发模式下的热更新速度提升非常明显尤其是项目膨胀到几十个页面之后那种改一行代码等两三秒的体验会让人崩溃Turbopack基本能做到毫秒级响应。初始化后的目录结构是这样一个格局my-fullstack-app/ ├── src/ │ ├── app/ │ │ ├── layout.tsx │ │ ├── page.tsx │ │ └── ... │ ├── components/ │ ├── lib/ │ └── server/ ├── public/ ├── .env.local └── package.jsonsrc/app是路由根目录src/components放可复用的UI组件src/lib放工具函数和数据库客户端src/server放服务端专用逻辑比如数据访问函数。这个分层看起来简单但当你项目有二十个页面、三十个组件之后它带来的心智负担极低。我见过有人把所有逻辑都塞进app目录里最后page.tsx动辄几百行改起来非常痛苦。2. App Router核心概念从路由到数据流2.1 页面路由与布局系统的正确打开方式App Router的文件约定是整个框架的基石理解了它你就理解了Next.js一半的设计哲学。每个文件夹对应一个路由段page.tsx是这个路由的页面内容layout.tsx是包裹子路由的布局。比如你的app目录长这样app/ ├── layout.tsx ├── page.tsx → / ├── blog/ │ ├── page.tsx → /blog │ └── [slug]/ │ └── page.tsx → /blog/:slug └── dashboard/ ├── layout.tsx → dashboard专属布局 └── page.tsx → /dashboardapp/layout.tsx是根布局整个应用所有页面都会被它包裹。dashboard/layout.tsx则只作用于/dashboard下面的路由。这种嵌套布局的好处是你可以把侧边栏、导航这些公共UI提取成布局组件Next.js在路由切换时会自动复用它们不会重新渲染性能上比每个页面各自引入导航组件要好得多。我实际项目里的做法是根布局放全局字体和Metadatadashboard布局放侧边导航和用户信息页面组件里只放业务内容。动态路由用方括号语法[slug]就是动态段。在页面里通过params拿到参数// app/blog/[slug]/page.tsx export default async function BlogPostPage({ params, }: { params: Promise{ slug: string }; }) { const { slug } await params; const post await getPostBySlug(slug); return article{post.title}/article; }注意15版本里params变成了Promise需要await一下。这是15版本的一个Breaking Change网上很多14版本的教程还没更新你在看资料时要注意辨别。除此之外还有几个约定文件值得记住loading.tsx定义页面加载时的UIerror.tsx定义运行时错误的UInot-found.tsx定义404页面。这几个文件让Next.js自动处理加载态和错误态不用在每个页面里手动写try...catch和loading判断体验上非常接近一个完整的SPA框架但实现方式又完全是服务端友好的。2.2 服务端组件与客户端组件边界划分实战这是App Router最核心、也最容易搞混的概念。默认情况下App Router里所有组件都是服务端组件它们在服务器上执行可以直接访问数据库、读取环境变量但不支持useState、useEffect这些客户端交互Hook。当你在组件文件顶部写上use client这个组件就变成了客户端组件拥有完整的React交互能力但它会在浏览器里执行不能直接访问服务端资源。很多人刚接触时的第一反应是“那就到处加use client呗”这是最大的误区。客户端组件会打包更多的JavaScript到浏览器增加首屏加载时间也失去服务端渲染的SEO优势。我实际项目的划分原则特别简单静态展示、需要SEO的页面 → 服务端组件需要查询数据的页面 → 服务端组件直接查表单、筛选器、交互性强的UI → 客户端组件同时需要交互又需要数据的组件 → 客户端组件通过API或Server Action拿数据举一个实际例子博客的评论区评论列表是服务端组件直接渲染评论表单是一个客户端组件提交时调用Server Action写入数据库然后通过revalidatePath刷新评论列表。这样整个页面只有一个表单是客户端的其他部分全是服务端渲染加载快、SEO好、代码也直观。还有个容易踩坑的点客户端组件里不能导入服务端组件但可以通过children传进来。这叫“服务端组件作为客户端组件的children”模式在Next.js官方文档里有明确推荐。比如你的客户端组件DashboardNav要包裹服务端组件AnalyticsChart正确写法是// app/dashboard/layout.tsx import DashboardNav from /components/DashboardNav; import AnalyticsChart from /components/AnalyticsChart; export default function DashboardLayout({ children }: { children: React.ReactNode }) { return ( div DashboardNav{children}/DashboardNav /div ); }children虽然在客户端组件里但它本身是服务端渲染好的内容不会被强制转成客户端组件。这个模式用好了可以极大减少客户端组件的数量。2.3 路由处理器不止是API如果你需要提供REST API给外部系统调用或者给客户端组件提供数据接口Route Handlers是正路。在任意路由文件夹下创建一个route.ts文件导出GET、POST等方法即可// app/api/posts/route.ts import { NextResponse } from next/server; import { getPosts, createPost } from /server/posts; export async function GET() { const posts await getPosts(); return NextResponse.json(posts); } export async function POST(request: Request) { const body await request.json(); const post await createPost(body); return NextResponse.json(post, { status: 201 }); }这些函数默认在服务端运行可以直接访问数据库不需要额外搭一层服务。配合动态段还能做/api/posts/[id]/route.ts这样的资源接口。但我要提醒一点App Router里page.tsx和route.ts不能放在同一个文件夹里会直接冲突报错需要把API路由放到app/api子目录下。另外Route Handlers支持配置运行时和缓存策略。默认情况下GET请求会被缓存在Next.js 15里行为有调整需要注意如果你要动态数据可以显式声明export const dynamic force-dynamic;或者用export const revalidate 0。这里有个我一开始被坑过的地方我以为GET的缓存行为是全局统一的实际上它取决于路由是否使用动态函数比如cookies()、headers()用了就自动变动态不用就可能被缓存。理解这个机制后API路由的缓存问题就好排查了。3. 数据层设计与集成3.1 数据获取模式的选型逻辑Next.js给了你四种数据获取方式静态生成SSG、服务端渲染SSR、增量静态再生ISR、客户端获取CSR。很多人一上来就纠结用哪种其实判断标准非常简单数据基本不变或变化很慢 → SSG构建时生成一次数据实时性强、跟用户请求相关 → SSR每次请求都渲染数据会变但可以接受一定延迟 → ISR定时重新生成数据完全私有、需要登录后加载 → CSR客户端获取举个典型场景产品官网的首页文案构建时生成就行选SSG用户仪表盘的统计数据选SSR或者客户端获取博客文章列表有一定实时性但又不想每次都重渲染选ISR比如revalidate 3600每小时重建一次。代码层面SSG和SSR在App Router里几乎没区别都是服务端组件里直接写await fetch()或数据库查询区别只在于你在运行时有没有启用动态行为。真正需要主动控制的场景是ISR只需在页面或路由里指定revalidate// app/blog/page.tsx export const revalidate 3600; export default async function BlogPage() { const posts await getPosts(); return PostList posts{posts} /; }这个配置意味着页面每小时重新验证一次验证后如果有更新就生成新版本用户访问时不会看到构建时写死的旧内容。对于内容型网站ISR真的能帮你省下一大笔服务器资源。3.2 数据库接入与ORM选型Next.js本身不绑定数据库你可以用任何方式接数据直接写pg连PostgreSQL用MongoDB官方驱动甚至用SQLite做本地开发。但我强烈建议用ORM原因有两个一是类型安全二是迁移管理。没有ORM的项目你写SQL改表结构时全靠记忆生产环境一旦跑个ALTER TABLE少了字段排查成本极高。我目前主力用Prisma生态成熟、文档详尽、类型生成体验一流。接入步骤非常固定npm install prisma prisma/client npx prisma init在prisma/schema.prisma里定义模型model Post { id String id default(cuid()) title String slug String unique content String published Boolean default(false) createdAt DateTime default(now()) updatedAt DateTime updatedAt }然后执行迁移并生成客户端npx prisma migrate dev --name init npx prisma generate在src/lib/prisma.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;这个globalThis缓存技巧是Prisma官方推荐的因为Next.js开发模式会频繁重新加载模块不缓存的话每次重载都会新建一个PrismaClient实例连接池很快就满了你会看到一个很尴尬的报错“Too many clients”。第一次遇到这个错误时我还以为是数据库配置有问题排查了半天才发现是连接数耗尽。3.3 缓存与重新验证策略全栈项目最头疼的问题永远是“改了数据页面没更新”。Next.js的缓存体系分好几层fetch的结果缓存、路由段的渲染缓存、路由器缓存、ISR缓存。新手经常遇到的症状是数据库内容已经变了但刷新页面还是旧数据。解决这个问题的标准化手段是revalidatePath和revalidateTag。以Server Action为例use server; import { revalidatePath } from next/cache; export async function createPost(formData: FormData) { const title formData.get(title); await prisma.post.create({ data: { title: String(title) } }); revalidatePath(/blog); }revalidatePath会清理指定路径的缓存让下次请求重新渲染。如果你想更精细地控制可以给fetch打标签const res await fetch(https://api.example.com/posts, { next: { tags: [posts] }, }); // 其他地方更新后 revalidateTag(posts);这里要特别提醒一个坑fetch的next: { tags }配置只在Next.js的fetch封装里生效如果你用Prisma直接查数据库是没有这套标签机制的只能依赖revalidatePath或unstable_cache。我项目的惯例是页面直接查数据库的更新后调用revalidatePath第三方API的用revalidateTag。这个套路稳定跑了一年多没再出现过“数据改了页面不变”的问题。4. 认证授权与安全性4.1 Session vs JWT在Next.js中的落地认证方案的选择会直接影响项目复杂度。Session方案是服务端存状态、客户端存session ID优点是能主动踢人、改权限即时生效缺点是需要服务端存储Redis或数据库JWT方案是无状态、客户端持有令牌优点是天然适合分布式缺点是token一旦签发难以撤销安全性上稍弱。在Next.js全栈项目里我推荐Session方案因为你可以直接利用Server Actions和Server Components的能力把用户信息放在cookie里服务端组件里随时读取。具体落地我用的是jose这个库注意不是jsonwebtoken因为后者在Edge Runtime不兼容import { SignJWT, jwtVerify } from jose; const secret new TextEncoder().encode(process.env.AUTH_SECRET); export async function createSession(userId: string) { const token await new SignJWT({ userId }) .setProtectedHeader({ alg: HS256 }) .setExpirationTime(7d) .sign(secret); cookies().set(session, token, { httpOnly: true, secure: process.env.NODE_ENV production, sameSite: lax, path: /, }); }httpOnly和sameSite: lax这两个配置是安全底线httpOnly防止JavaScript读取cookie挡XSSsameSite防止跨站请求携带cookie挡CSRF。我见过不少新手项目直接把用户信息放在localStorage里这等于给XSS攻击开了大门。如果不想自己造轮子直接上NextAuth.js现在叫Auth.js也可以。它内置了Google、GitHub等OAuth provider还支持Credentials登录能省掉很多底层工作。但它也有学习成本尤其是适配App Router时需要安装next-authbeta版本。我自己的建议是项目登录方式简单就账号密码或单一OAuth自己实现更可控项目需要集成多家OAuth用Auth.js更划算。4.2 中间件与路由保护Next.js的中间件运行在Edge Runtime里在每个请求到达页面或Route Handler之前执行适合做粗粒度的路由保护和重定向逻辑。在项目根目录或src目录下创建middleware.ts// src/middleware.ts import { NextResponse } from next/server; import type { NextRequest } from next/server; export function middleware(request: NextRequest) { const session request.cookies.get(session); const { pathname } request.nextUrl; const isProtected pathname.startsWith(/dashboard); const isAuthPage pathname.startsWith(/login); if (isProtected !session) { return NextResponse.redirect(new URL(/login, request.url)); } if (isAuthPage session) { return NextResponse.redirect(new URL(/dashboard, request.url)); } return NextResponse.next(); } export const config { matcher: [/dashboard/:path*, /login], };中间件只是一个前置拦截真正的鉴权还是要放在服务端组件或Route Handler里再验证一次session的合法性。原因很简单中间件只检查cookie是否存在如果cookie是伪造的中间件放行了但页面里验证签名会发现是无效的。安全设计的原则是“永远不要相信前置检查能拦住所有攻击”核心数据的访问必须依赖深层校验。还有一点要注意中间件运行在Edge环境不能直接使用Node.js的crypto、fs、数据库连接等模块所以验证session签名的代码要选择兼容Edge的库。jose就满足这个条件这也是我推荐它而不是jsonwebtoken的原因。4.3 常见的几类安全隐患全栈开发最怕的不是功能做不出来而是数据被拖了还不知道。我在好几个项目里都见过类似的问题这里集中列一下第一没有做输入校验。用request.json()拿到数据直接写数据库攻击者传一个超长字符串或者恶意字段就可能导致报错或注入。我的做法是用了zod库做schema校验import { z } from zod; const postSchema z.object({ title: z.string().min(1).max(100), content: z.string().min(1).max(50000), }); const result postSchema.safeParse(body); if (!result.success) { return NextResponse.json({ error: result.error }, { status: 400 }); }第二Server Actions暴露了太多内部逻辑。Server Actions本质上是一个POST接口任何人可以在浏览器控制台里调用它。如果你的Action里没有再次校验当前用户是否有权限执行这个操作那权限漏洞就是必然的。每个Server Action的第一步都应该验证“当前登录用户是谁、他有没有权限做这件事”。第三敏感信息写死在客户端组件里。任何以NEXT_PUBLIC_开头的环境变量都会被发送到浏览器如果你不小心把API密钥、数据库密码放进了NEXT_PUBLIC_变量这些秘密就全部暴露了。我的铁律是只有确实需要在浏览器里用的公开配置比如某个地图SDK的公开key才用NEXT_PUBLIC_其他一律只用服务端环境变量。5. 部署与性能优化实战5.1 环境变量与多环境配置环境变量管理看起来简单实际坑不少。Next.js的环境变量体系有几个层级.env.local是本地开发.env.production是生产构建.env.test是测试环境。变量分两类以NEXT_PUBLIC_开头的是公开变量会打进浏览器端的bundle里不带前缀的是服务端变量。我在项目里通常维护四份文件而且会把这些文件加到.gitignore里避免密钥进仓库.env.local # 本地开发含真实数据库地址 .env.local.example # 提交到仓库只含变量名占位 .env.production # 生产环境部署平台上配置部署平台的配置方式各不相同Vercel里直接在Project Settings → Environment Variables里添加即可不用上传文件。注意一个细节本地.env.local和远程配置的变量名必须保持一致否则构建时容易出现“某个变量未定义但本地一切正常”的诡异问题。5.2 部署方案对比与选择Next.js的部署选项很丰富但核心其实就三条路线Vercel托管、Node.js自建服务器、Docker容器化。Vercel是官方推荐的一站式方案优势是零配置、自动构建、全球CDN免费额度对个人项目完全够用。我的博客和个人工具站都放在Vercel上push到GitHub就自动部署体验极其爽利。但要注意的是Vercel是Serverless函数模型每个请求可能落在不同的实例上本地文件系统不能持久化比如你写入服务器的一个文件下次请求可能读不到数据库或对象存储必须使用外部服务。自建服务器适合有现成VPS的情况。用next start启动生产构建前面挂一层Nginx反代再用PM2守护进程方案成熟可靠。这种方式的优点是灵活、成本可控缺点是要自己处理SSL证书、日志轮转、进程守护这些运维工作。Docker方案是目前团队项目的主流。一个标准DockerfileFROM node:20-alpine AS base FROM base AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci FROM base AS builder WORKDIR /app COPY --fromdeps /app/node_modules ./node_modules COPY . . RUN npm run build FROM base AS runner WORKDIR /app ENV NODE_ENVproduction COPY --frombuilder /app/.next/standalone ./ COPY --frombuilder /app/.next/static ./.next/static COPY --frombuilder /app/public ./public EXPOSE 3000 CMD [node, server.js]这里的关键是output: standalone配置需要在next.config.ts里开启const nextConfig { output: standalone, }; export default nextConfig;Standalone模式会生成一个自包含的服务端文件体积小、启动快不需要node_modules依赖非常适合Docker镜像。我在生产环境测试过镜像体积最小能压到200MB以内比直接用完整node_modules的方案小了不止一半。5.3 性能监控与优化清单性能优化是持续工程不是上线前突击一下就行。基础指标看两个LCP最大内容绘制和CLS布局偏移。Next.js内置了next/image组件做图片优化自动转WebP/AVIF、自动响应式尺寸、懒加载这些能力开箱即用前提是你别偷懒直接用原生img。我完整跑过一个优化清单按收益排序是这样的图片全部换成next/image配置好sizes和priority。这个优化通常能让LCP下降30%以上。字体用next/font加载它做了字体子集化和预加载避免字体文件拖慢首屏。减少客户端组件数量。每多一个use client浏览器就要多下载一份该组件的JavaScript。我复盘过自己的项目有些交互按钮其实可以放进服务端组件里配合Server Action完成操作完全不需要客户端化。用Suspense拆分长页面。尤其是数据查询耗时较长的区块包在Suspense里可以避免整个页面都陷入等待。开启Turbopack构建。Next.js 15里next build --turbopack已经可用构建速度提升明显CI流水线的时间能省很多。性能优化最忌讳盲目迷信某个指标而是先看Lighthouse和Web Vitals的真实数据再针对瓶颈下手。我见过有人花一天时间优化bundle体积结果发现真正的瓶颈是首屏图片没有懒加载方向完全错了。6. 常见问题与避坑实录6.1 开发环境中的疑难杂症开发阶段遇到最多的三类问题我一个个说。第一类是“环境变量变了但代码里读不到”。process.env.SOME_KEY在服务端组件里读不到但终端里echo $SOME_KEY是有的。这通常是因为你改完.env.local没有重启开发服务器Next.js只在启动时加载环境变量文件改了文件必须重启npm run dev。注意这是开发模式的行为生产环境每次构建时重新读取所以部署时不会有这个问题但本地会。第二类是HMR卡死或样式错乱。如果你同时用了Tailwind和其他CSS方案偶尔会遇到修改类名后样式不刷新的情况。我的处理办法是直接删掉.next目录重启虽然粗暴但非常有效rm -rf .next npm run dev第三类是路径别名报错。/*在tsconfig.json里配置的路径映射有时你在新增文件后忘了配相应目录或者在服务端引入了客户端组件就会突然报类型错误。维护这个映射别偷懒统一都用/components、/lib这种语义化路径。6.2 生产环境踩坑记录上线后暴露的问题和本地完全是两回事印象最深的是“内存泄漏”。我的一个数据展示页面在本地跑几十个小时没问题部署到服务器上运行两天后内存暴涨。排查下来是unstable_cache的过期时间配置不当加上某个setInterval在服务端组件里被反复调度。服务端组件里千万不要写setInterval它不会像浏览器里那样自动清理每个请求都可能创建一个新定时器最终拖垮进程。第二个坑是“白屏”。现象是首页偶尔变成空白刷新又好了。原因是React在服务端渲染时产生的结果没法在客户端匹配上所谓hydration mismatch。服务端生成HTML时的随机数、Date.now()、当前时间这类动态值和客户端首次渲染时的结果不一致就会导致这个错。解决方法是这些值只在客户端渲染时生成const [time, setTime] useStatestring | null(null); useEffect(() { setTime(new Date().toISOString()); }, []);或者在组件里用suppressHydrationWarning但这是治标不治本真正原因是你在服务端渲染里放进去了不可预测的值。第三个坑是“数据库连接数被打满”。Serverless环境下实例会并发启动每个实例都创建数据库连接连接数很快就超限了。解决方案是控制连接池上限并让Prisma在全局复用实例就是我前面提到的globalThis写法。如果你用的是自托管PostgreSQL还可以在连接串上加上?connection_limit5这样的参数限制连接数。6.3 新手最容易犯的五个错误我回看自己入门时的代码和帮别人review的代码有五类错误出现频率极高。第一把所有组件都标记成客户端组件。这个前面已经说过了带来的问题就是首屏JavaScript体积越来越大页面加载越来越慢。判断标准很简单组件里没有useState、useEffect、事件处理就不要加use client。第二在客户端组件里直接查询数据库。客户端组件不能访问服务端环境变量和数据库有些新手尝试直接import { prisma } from /lib/prisma然后在客户端组件里用会立刻报“PrismaClient is unable to run in this browser environment”的错误。正确做法是客户端组件通过Route Handler或Server Action拿数据。第三忽略加载态和错误态。页面虽然会自动生成loading.tsx和error.tsx的插槽但如果你不提供这些文件用户在数据加载时会看到一片空白出错时直接看到报错页面。每个路由段都应该安排这两个文件。第四fetch请求写死完整URL。服务端组件的fetch里如果写fetch(http://localhost:3000/api/...)部署后就变成请求自己服务器了既慢又容易出问题。正确处理是如果是外部API用绝对URL如果是自己的接口服务端直接调用逻辑函数不要把页面裹一层自己的HTTP请求。第五忽略key的正确使用。在列表渲染时不用key或者用数组下标当key会导致React的diff算法出错出现渲染错乱、状态复用问题。尤其当列表会增删排序时下标key会造成严重的UI bug。正确做法是用数据项的唯一ID。6.4 一个完整项目的生命周期复盘最后用一个真实项目的脉络来串起前面所有知识点。我最近做了一个内部工具站有登录、数据看板、内容管理三块功能。技术栈就是Next.js 15 TypeScript Prisma PostgreSQL Tailwind部署在Vercel上。开发顺序是先建好Prisma模型和迁移再搭认证模块注册、登录、session管理再写核心业务页面服务端组件查数据渲染最后加客户端交互模块表单、图表。整个过程中没有写任何一行后端路由代码所有数据变更都走Server Action配合revalidatePath刷新UI。项目从零到上线大概用了一周其中一半时间花在业务逻辑和样式上框架本身几乎没有成为瓶颈。回看这个项目的技术决策我认为正确的选择是App Router带来的服务端组件能力让绝大部分页面都不需要客户端JavaScriptServer Action简化了数据变更链路不用在API路由和状态管理之间来回接线Prisma的类型安全让schema变更时编译器就能揪出所有受影响的位置。这三件事组合起来对个人开发者和小团队的效率提升是革命性的。如果你正准备用Next.js做一个新项目我的建议是先花一天时间把App Router的约定文件和服务端/客户端组件的边界彻底搞清楚再动手。这个投入很值得方向对了后面所有开发都会非常顺畅。
返回列表