📖 核心概念
- SSR vs SSG vs ISR vs CSR对比
- Next.js App Router架构
- Server Components与Client Components
- 数据获取策略
- API Routes与Server Actions
- SEO与元数据管理
💻 代码实现
Next.js App Router
✅ tsx
// app/layout.tsx - 根布局 (Server Component)
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: { default: '我的应用', template: '%s | 我的应用' },
description: '使用Next.js构建的现代应用',
keywords: ['React', 'Next.js', 'TypeScript'],
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh-CN">
<body>{children}</body>
</html>
);
}
// app/page.tsx - 首页 (Server Component)
async function HomePage() {
// 服务端直接获取数据,无需useEffect
const posts = await fetch('https://api.example.com/posts', {
next: { revalidate: 3600 }, // ISR: 1小时后重新验证
}).then(r => r.json());
return (
<main>
<h1>最新文章</h1>
{posts.map((post: any) => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</main>
);
}
// app/blog/[slug]/page.tsx - 动态路由 (SSG)
interface BlogPostProps { params: { slug: string } }
// 生成静态路径
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json());
return posts.map((post: any) => ({ slug: post.slug }));
}
// 生成元数据
export async function generateMetadata({ params }: BlogPostProps) {
const post = await fetch(\`https://api.example.com/posts/\${params.slug}\`).then(r => r.json());
return {
title: post.title,
description: post.excerpt,
openGraph: { title: post.title, description: post.excerpt },
};
}
export default async function BlogPost({ params }: BlogPostProps) {
const post = await fetch(\`https://api.example.com/posts/\${params.slug}\`, {
next: { revalidate: 3600 },
}).then(r => r.json());
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
Server Actions与Client Components
✅ tsx
// app/actions.ts - Server Actions
'use server';
import { revalidatePath } from 'next/cache';
import { redirect } from 'next/navigation';
import { z } from 'zod';
const createPostSchema = z.object({
title: z.string().min(1),
content: z.string().min(10),
});
export async function createPost(formData: FormData) {
const data = createPostSchema.parse({
title: formData.get('title'),
content: formData.get('content'),
});
await db.post.create({ data });
revalidatePath('/blog'); // 刷新博客页面缓存
redirect('/blog'); // 重定向到博客列表
}
export async function deletePost(id: string) {
await db.post.delete({ where: { id } });
revalidatePath('/blog');
}
// app/blog/create/page.tsx - 表单使用Server Action
import { createPost } from '@/app/actions';
export default function CreatePostPage() {
return (
<form action={createPost} className="form">
<input name="title" placeholder="标题" required />
<textarea name="content" placeholder="内容" required />
<button type="submit">发布文章</button>
</form>
);
}
// components/PostEditor.tsx - Client Component
'use client';
import { useTransition } from 'react';
import { deletePost } from '@/app/actions';
export function DeleteButton({ postId }: { postId: string }) {
const [isPending, startTransition] = useTransition();
return (
<button
disabled={isPending}
onClick={() => startTransition(() => deletePost(postId))}
>
{isPending ? '删除中...' : '删除'}
</button>
);
}
// app/api/route.ts - API Routes
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const page = searchParams.get('page') || '1';
const posts = await db.post.findMany({
skip: (Number(page) - 1) * 10,
take: 10,
});
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 });
}
🎯 练习任务
- 1 创建一个Next.js博客,包含SSG首页和SSR文章页
- 2 实现Server Actions处理表单提交
- 3 配置ISR策略,平衡实时性与性能
- 4 添加Open Graph元数据,优化社交分享效果
🏆 成就解锁
Next.js开发者 — 掌握Next.js全栈开发,构建高性能的React应用