返回博客

Next.js 15 App Router 完全指南

全面了解 Next.js 15 的 App Router,包括路由、数据获取、缓存策略等核心概念。

Next.js 15 App Router 完全指南
4 天前
5 分钟阅读
370 次浏览

Next.js 15 App Router 完全指南

Next.js 15 的 App Router 带来了全新的应用架构和更强大的功能。让我们深入了解如何充分利用它。

目录结构

text
app/ ├── page.tsx # 首页 / ├── layout.tsx # 根布局 ├── loading.tsx # 加载状态 ├── error.tsx # 错误处理 ├── blog/ │ ├── page.tsx # /blog │ ├── [slug]/ │ │ └── page.tsx # /blog/[slug] │ └── layout.tsx # 博客布局 └── api/ └── posts/ └── route.ts # API 路由

Server Components(默认)

tsx
// app/blog/page.tsx async function BlogPage() { // 直接在组件中获取数据 const posts = await db.post.findMany(); return ( <div> {posts.map(post => ( <PostCard key={post.id} post={post} /> ))} </div> ); } export default BlogPage;

数据获取策略

1. 静态生成(推荐)

tsx
// 构建时生成 export const dynamic = 'force-static'; async function StaticPage() { const data = await fetch('https://api.example.com/data'); return <div>{/* ... */}</div>; }

2. 动态渲染

tsx
// 每次请求时生成 export const dynamic = 'force-dynamic'; async function DynamicPage() { const data = await fetch('https://api.example.com/data', { cache: 'no-store' }); return <div>{/* ... */}</div>; }

3. 增量静态再生成(ISR)

tsx
// 每 60 秒重新验证 async function ISRPage() { const data = await fetch('https://api.example.com/data', { next: { revalidate: 60 } }); return <div>{/* ... */}</div>; }

并行数据获取

tsx
async function ParallelDataPage() { // 并行获取多个数据源 const [posts, users, comments] = await Promise.all([ fetchPosts(), fetchUsers(), fetchComments(), ]); return ( <div> <Posts data={posts} /> <Users data={users} /> <Comments data={comments} /> </div> ); }

流式渲染与 Suspense

tsx
import { Suspense } from 'react'; async function Posts() { const posts = await fetchPosts(); // 慢速数据 return <PostList posts={posts} />; } function Page() { return ( <div> <Header /> {/* 立即显示 */} <Suspense fallback={<LoadingPosts />}> <Posts /> {/* 异步加载 */} </Suspense> </div> ); }

路由处理

动态路由

tsx
// app/blog/[slug]/page.tsx export async function generateStaticParams() { const posts = await getPosts(); return posts.map(post => ({ slug: post.slug })); } async function BlogPost({ params }: { params: { slug: string } }) { const post = await getPost(params.slug); return <article>{post.content}</article>; }

路由组

text
app/ ├── (marketing)/ │ ├── page.tsx # / │ └── about/ │ └── page.tsx # /about └── (shop)/ ├── products/ └── cart/

Metadata API

tsx
import { Metadata } from 'next'; export const metadata: Metadata = { title: 'My Blog', description: 'A blog about web development', openGraph: { title: 'My Blog', description: 'A blog about web development', images: ['/og-image.jpg'], }, }; // 动态 metadata export async function generateMetadata({ params }): Promise<Metadata> { const post = await getPost(params.slug); return { title: post.title, description: post.excerpt, }; }

API Routes

tsx
// app/api/posts/route.ts import { NextRequest, NextResponse } from 'next/server'; export async function GET(request: NextRequest) { const posts = await db.post.findMany(); return NextResponse.json(posts); } export async function POST(request: NextRequest) { const body = await request.json(); const post = await db.post.create({ data: body }); return NextResponse.json(post, { status: 201 }); }

缓存策略

Next.js 15 提供了多层缓存:

  1. Request Memoization:同一请求中的相同 fetch 自动去重
  2. Data Cache:跨请求的持久化缓存
  3. Full Route Cache:静态路由的完整缓存
  4. Router Cache:客户端导航缓存

性能优化技巧

1. 使用 loading.tsx

tsx
// app/blog/loading.tsx export default function Loading() { return <BlogSkeleton />; }

2. 图片优化

tsx
import Image from 'next/image'; <Image src="/hero.jpg" alt="Hero" width={800} height={600} priority // 首屏图片 />

3. 字体优化

tsx
import { Inter } from 'next/font/google'; const inter = Inter({ subsets: ['latin'] }); export default function RootLayout({ children }) { return ( <html lang="en" className={inter.className}> <body>{children}</body> </html> ); }

总结

Next.js 15 的 App Router 提供了:

  • 更好的性能(Server Components)
  • 更灵活的数据获取
  • 内置的流式渲染
  • 强大的缓存控制
  • 更好的开发体验

标签:Next.js, React, Performance

周温

周温

全栈开发工程师,专注于前端技术栈和物联网应用开发。拥有丰富的 React、Next.js、Nest.js 项目经验,擅长构建高性能、可扩展的 Web 应用。

评论 (0)

请先登录后再发表评论

登录

还没有评论,来发表第一条吧!