·20 分钟
Next.js 15 全栈开发实战指南
Next.jsReact全栈TypeScriptServer Components全栈
Next.js 15 核心特性
App Router 架构
App Router 是 Next.js 13+ 引入的新路由系统,基于文件系统的路由,支持嵌套布局、加载状态、错误处理等。
code
app/
├── layout.tsx # 根布局
├── page.tsx # 首页
├── loading.tsx # 加载状态
├── error.tsx # 错误处理
├── not-found.tsx # 404 页面
├── blog/
│ ├── layout.tsx # 博客布局
│ ├── page.tsx # 博客列表
│ └── [slug]/
│ └── page.tsx # 博客详情
└── api/
└── users/
└── route.ts # API 路由
Server Components vs Client Components
tsx
// Server Component (默认) - 服务端渲染
// 可以直接访问数据库、文件系统等
async function BlogList() {
const posts = await db.query('SELECT * FROM posts');
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
// Client Component - 客户端渲染
// 需要 'use client' 指令
'use client';
import { useState } from 'react';
function LikeButton({ postId }: { postId: string }) {
const [likes, setLikes] = useState(0);
return (
<button onClick={() => setLikes(likes + 1)}>
👍 {likes}
</button>
);
}
Server Actions 详解
Server Actions 是 Next.js 14+ 引入的服务端函数,可以直接在组件中调用服务端逻辑。
tsx
// app/actions/posts.ts
'use server';
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
await db.posts.create({
data: { title, content }
});
revalidatePath('/blog');
}
export async function deletePost(postId: string) {
await db.posts.delete({ where: { id: postId } });
revalidatePath('/blog');
}
tsx
// app/blog/create/page.tsx
import { createPost } from '@/app/actions/posts';
export default function CreatePostPage() {
return (
<form action={createPost}>
<input name="title" placeholder="文章标题" required />
<textarea name="content" placeholder="文章内容" required />
<button type="submit">发布文章</button>
</form>
);
}
数据获取策略
服务端数据获取
tsx
// 静态生成 (SSG)
async function BlogPost({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
return <article>{post.content}</article>;
}
// 动态渲染 (SSR)
async function UserDashboard() {
// 强制动态渲染
const session = await getServerSession();
return <div>Welcome, {session.user.name}</div>;
}
// 增量静态再生 (ISR)
async function ProductPage({ params }: { params: { id: string } }) {
const product = await getProduct(params.id);
return <ProductDetail product={product} />;
}
// 重新验证时间
export const revalidate = 3600; // 每小时重新生成
客户端数据获取
tsx
'use client';
import useSWR from 'swr';
function UserList() {
const { data, error, isLoading } = useSWR('/api/users', fetcher);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
API Routes 实现
typescript
// app/api/users/route.ts
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '10');
const users = await db.users.findMany({
skip: (page - 1) * limit,
take: limit,
});
return NextResponse.json(users);
}
export async function POST(request: Request) {
const body = await request.json();
const user = await db.users.create({
data: body,
});
return NextResponse.json(user, { status: 201 });
}
typescript
// app/api/users/[id]/route.ts
import { NextResponse } from 'next/server';
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const user = await db.users.findUnique({
where: { id: params.id },
});
if (!user) {
return NextResponse.json(
{ error: 'User not found' },
{ status: 404 }
);
}
return NextResponse.json(user);
}
export async function PUT(
request: Request,
{ params }: { params: { id: string } }
) {
const body = await request.json();
const user = await db.users.update({
where: { id: params.id },
data: body,
});
return NextResponse.json(user);
}
export async function DELETE(
request: Request,
{ params }: { params: { id: string } }
) {
await db.users.delete({
where: { id: params.id },
});
return NextResponse.json(null, { status: 204 });
}
中间件与认证
typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { verifyToken } from '@/lib/auth';
export async function middleware(request: NextRequest) {
const token = request.cookies.get('token')?.value;
// 保护路由
if (request.nextUrl.pathname.startsWith('/dashboard')) {
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
try {
await verifyToken(token);
return NextResponse.next();
} catch {
return NextResponse.redirect(new URL('/login', request.url));
}
}
// API 路由认证
if (request.nextUrl.pathname.startsWith('/api')) {
if (!token) {
return NextResponse.json(
{ error: 'Unauthorized' },
{ status: 401 }
);
}
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*'],
};
部署与优化
环境变量配置
bash
# .env.local
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
NEXTAUTH_SECRET="your-secret-key"
NEXTAUTH_URL="http://localhost:3000"
性能优化
tsx
// 使用 Image 组件优化图片
import Image from 'next/image';
function Avatar({ src, alt }: { src: string; alt: string }) {
return (
<Image
src={src}
alt={alt}
width={48}
height={48}
placeholder="blur"
blurDataURL="/placeholder.png"
/>
);
}
// 使用 Link 组件预加载
import Link from 'next/link';
function Navigation() {
return (
<nav>
<Link href="/blog" prefetch={true}>
Blog
</Link>
</nav>
);
}
// 字体优化
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
总结
Next.js 15 提供了完整的全栈开发解决方案:
- App Router:基于文件系统的路由,支持嵌套布局
- Server Components:服务端渲染,减少客户端 JavaScript
- Server Actions:简化服务端逻辑,无需 API Routes
- 数据获取:支持 SSG、SSR、ISR 多种策略
- 中间件:请求拦截、认证、重定向
- 性能优化:图片、字体、代码分割
掌握这些核心特性,就能构建出高性能、可扩展的现代化 Web 应用。