
1. 问题概述Next.js 14 的 App Router 已成为 React 全栈开发的主流范式。Server Components、Server Actions、流式渲染等新概念让很多开发者面临学习曲线如何正确使用 App Router 是当前热门话题。2. 核心概念2.1 Server Components vs Client Components┌─────────────────────────────────────────┐ │ 页面组件 │ │ ┌─────────────────────────────────┐ │ │ │ Server Component默认 │ │ │ │ - 直接访问数据库 │ │ │ │ - 零客户端 JS │ │ │ └─────────────────────────────────┘ │ │ ┌─────────────────────────────────┐ │ │ │ Client Component │ │ │ │ - use client │ │ │ │ - 交互、状态、浏览器 API │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────────┘2.2 Server Actions// app/actions.ts use server import { revalidatePath } from next/cache export async function createPost(formData: FormData) { const title formData.get(title) await db.post.create({ data: { title } }) revalidatePath(/posts) }3. 最佳实践3.1 1. 数据获取// ✅ 推荐在 Server Component 中直接获取 async function PostList() { const posts await db.post.findMany() return posts.map(post PostCard key{post.id} post{post} /) } // ❌ 避免在 Client Component 中直接请求数据库3.2 2. 组件拆分策略页面 ├── Server Components数据获取、静态内容 │ ├── Client Components交互部分 │ └── Server Components更多静态内容 └── Client Components包装交互状态3.3 3. 流式渲染import { Suspense } from react export default function Page() { return ( div h1Dashboard/h1 Suspense fallback{Skeleton /} SlowComponent / /Suspense /div ) }3.4 4. 缓存策略// 静态渲染默认 // 动态渲染 export const dynamic force-dynamic // 重新验证 export const revalidate 3600 // 1小时4. 路由系统深入4.1 路由分组与布局// app/(marketing)/layout.tsx - 营销页面共享布局 exportdefaultfunctionMarketingLayout({ children }) { return ( div MarketingNav / {children} MarketingFooter / /div ); } // app/(dashboard)/layout.tsx - 后台页面共享布局 exportdefaultfunctionDashboardLayout({ children }) { return ( div classNameflex Sidebar / main classNameflex-1{children}/main /div ); }4.2 并行路由与拦截路由// app/layout.tsx exportdefaultfunctionLayout({ children, modal }) { return ( div {children} {modal} {/* 并行渲染的 modal */} /div ); } // app/modal/(.)photo/[id]/page.tsx - 拦截路由 exportdefaultfunctionPhotoModal({ params }) { return ( Modal Photo id{params.id} / /Modal ); }4.3 中间件实战// middleware.ts import { NextResponse } fromnext/server; importtype { NextRequest } fromnext/server; exportfunctionmiddleware(request: NextRequest) { const token request.cookies.get(token); const { pathname } request.nextUrl; // 路由保护 if (pathname.startsWith(/dashboard) !token) { returnNextResponse.redirect(newURL(/login, request.url)); } // 国际化路由 if (pathname /) { const locale request.headers.get(accept-language)?.split(,)[0] || en; returnNextResponse.redirect(newURL(/${locale}, request.url)); } returnNextResponse.next(); } exportconst config { matcher: [/dashboard/:path*, /], };5. 常见陷阱1.过度使用 use client导致 bundle 增大仅在需要交互、状态或浏览器 API 时使用2.Server Actions 滥用适合表单提交和数据变更不适合复杂状态管理和实时更新3.缓存理解不足需要清楚 Full Route Cache、Data Cache、Router Cache、Request Memoization 四层缓存4.中间件滥用Edge Runtime 有运行时限制不支持 Node.js API5.环境变量泄露客户端变量必须NEXT_PUBLIC_前缀敏感信息仅在服务端6. 性能优化6.1 图片优化import Image from next/image; // 自动优化WebP/AVIF 格式、懒加载、尺寸优化 Image src/hero.jpg altHero width{1200} height{600} priority // 首屏图片预加载 placeholderblur // 模糊占位 /6.2 字体优化import { Inter } from next/font/google; const inter Inter({ subsets: [latin], display: swap, variable: --font-inter, }); // 零布局偏移自动内联关键 CSS6.3 Partial Prerendering (PPR)// 静态外壳 动态内容流式注入 export const experimental_ppr true; export default function Page() { return ( div StaticShell / {/* 构建时预渲染 */} Suspense fallback{Skeleton /} DynamicContent / {/* 请求时动态渲染 */} /Suspense /div ); }6.4 Bundle 分析# 分析打包体积 ANALYZEtrue next build # 查看模块依赖 npx next-bundle-analyzer7. 数据变更模式7.1 Server Actions revalidatePath// app/actions.ts use server; import { revalidatePath, revalidateTag } from next/cache; export async function updatePost(id: string, data: FormData) { await db.post.update({ where: { id }, data: { title: data.get(title) } }); revalidatePath(/posts); revalidateTag(post-${id}); }7.2 乐观更新use client; import { useOptimistic } fromreact; import { updatePost } from./actions; functionPostEditor({ post }) { const [optimisticPost, setOptimistic] useOptimistic( post, (state, newTitle) ({ ...state, title: newTitle }) ); asyncfunctionhandleSubmit(formData) { const title formData.get(title); setOptimistic(title); // 立即更新 UI awaitupdatePost(post.id, formData); // 服务端更新 } return ( form action{handleSubmit} h2{optimisticPost.title}/h2 input nametitle defaultValue{post.title} / button typesubmit保存/button /form ); }8. 参考资源• Next.js 官方文档• Next.js App Router 课程• Vercel Templates