Next.js 15 基础教程
学习 Next.js 15 的核心概念,包括 App Router、Server Components、流式渲染等最新特性
什么是 Next.js?
Next.js 是一个基于 React 的全栈框架,由 Vercel 开发和维护。它为 React 应用提供了 生产级的功能和优化,让开发者能够轻松构建现代 Web 应用程序。
Next.js 解决了什么问题?
性能问题
- • SEO 优化困难
- • 首屏加载速度慢
- • 代码分割复杂
- • 图片优化手动处理
开发体验问题
- • 路由配置复杂
- • 构建配置繁琐
- • 热重载不稳定
- • TypeScript 配置困难
部署问题
- • 服务器配置复杂
- • CDN 配置手动
- • 环境变量管理困难
- • 缓存策略难以实现
全栈开发问题
- • API 路由分离
- • 数据获取复杂
- • 状态管理困难
- • 类型安全缺失
Next.js 15 的核心特性
App Router
基于文件系统的新路由方式,支持布局和嵌套路由
Server Components
服务端渲染的 React 组件,更好的性能和 SEO
流式渲染
渐进式页面加载,提升用户体验
App Router 文件系统路由
Next.js 15 使用 app 目录结构来定义路由,每个文件夹代表一个路由段
App Router 路由演示
Server Components vs Client Components
了解服务端组件和客户端组件的区别,以及何时使用它们
Server Component (服务端组件)
• 在服务器端渲染
• 可以直接访问数据库
• 更小的客户端包体积
• 无法使用浏览器 API
Client Component (客户端组件)
• 在浏览器中渲染
• 可以使用 React 状态和生命周期
• 支持交互和事件处理
• 使用 'use client' 指令
Loading 和 Error 边界
Next.js 15 提供了内置的 loading 和 error 处理机制
内容已加载完成
动态路由和参数处理
体验 Next.js 的动态路由功能,包括路径参数和查询参数的处理
当前路由信息:
{ "slug": "nextjs-guide" }
{ "category": "tech", "tags": "react,nextjs" }
现代化数据获取
演示 Next.js 15 中的各种数据获取方式和缓存策略
服务端组件 演示
中间件 (Middleware)
体验 Next.js 中间件的强大功能,包括认证、权限控制和请求处理
用户状态
路由导航
中间件执行日志
当前页面状态
当前路径: /dashboard
认证状态: 未登录
用户角色: guest
页面类型: 受保护页面
路由系统:App Router vs Pages Router
Next.js 提供了两种路由系统:传统的 Pages Router 和新的 App Router。 App Router 是 Next.js 13+ 引入的新路由系统,提供了更强大的功能。
Pages Router (传统)
├── index.js → /
├── about.js → /about
├── blog/
│ ├── index.js → /blog
│ └── [slug].js → /blog/:slug
└── api/
└── users.js → /api/users
- • 简单直观的文件路由
- • getServerSideProps
- • getStaticProps
- • 成熟稳定
App Router (推荐)
├── layout.js → 根布局
├── page.js → /
├── about/
│ └── page.js → /about
├── blog/
│ ├── layout.js → 博客布局
│ ├── page.js → /blog
│ └── [slug]/
│ └── page.js → /blog/:slug
└── api/
└── users/
└── route.js → /api/users
- • 支持嵌套布局
- • Server Components
- • 流式渲染
- • 更强大的数据获取
// App Router 示例
// app/layout.tsx - 根布局
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="zh">
<body>
<header>全局导航</header>
{children}
<footer>全局页脚</footer>
</body>
</html>
);
}
// app/blog/layout.tsx - 嵌套布局
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="blog-container">
<aside>博客侧边栏</aside>
<main>{children}</main>
</div>
);
}
// app/blog/[slug]/page.tsx - 动态路由
export default function BlogPost({
params,
}: {
params: { slug: string };
}) {
return <h1>博客文章: {params.slug}</h1>;
}
渲染模式对比与选择
深度理解 SSR、SSG、ISR、CSR 四种渲染模式的特点、优势和适用场景
SSR (服务端渲染)
每次请求时在服务器渲染页面
优势
- ✓SEO 友好
- ✓首屏加载快
- ✓实时数据
劣势
- ⚠服务器负载高
- ⚠TTFB 较慢
- ⚠缓存困难
渲染流程
渲染模式选择指南
Next.js 支持多种渲染模式,每种模式都有其特定的使用场景和优势。 了解这些模式可以帮助你选择最适合的渲染策略。
SSR (服务端渲染)
特点: 每次请求时在服务器生成 HTML
适用: 需要实时数据的页面
优势: SEO 友好,首屏快速显示
劣势: 服务器负载高,TTFB 较慢
SSG (静态生成)
特点: 构建时预生成静态 HTML
适用: 内容相对固定的页面
优势: 性能最佳,CDN 友好
劣势: 需要重新构建更新内容
ISR (增量静态再生)
特点: 静态生成 + 后台更新
适用: 需要定期更新的静态内容
优势: 兼顾性能和内容新鲜度
劣势: 实现相对复杂
CSR (客户端渲染)
特点: 在浏览器中渲染内容
适用: 高度交互的应用界面
优势: 丰富的交互体验
劣势: SEO 需要特殊处理
// SSR 示例 (App Router)
export default async function ProductPage({ params }: { params: { id: string } }) {
// 每次请求时获取数据
const product = await getProduct(params.id);
return <ProductDetails product={product} />;
}
// SSG 示例
export async function generateStaticParams() {
const products = await getProducts();
return products.map((product) => ({
id: product.id,
}));
}
export default async function StaticProductPage({ params }: { params: { id: string } }) {
// 构建时生成静态页面
const product = await getProduct(params.id);
return <ProductDetails product={product} />;
}
// ISR 示例 (Pages Router)
export async function getStaticProps() {
const data = await fetchData();
return {
props: { data },
revalidate: 60, // 60秒后重新生成
};
}
数据获取策略
Next.js 提供了多种数据获取方法,支持不同的渲染策略和使用场景。 App Router 简化了数据获取,使其更加直观和强大。
App Router 数据获取 (推荐)
服务端组件
- • 直接使用 async/await
- • 自动缓存优化
- • 支持并行数据获取
- • 零客户端 JavaScript
客户端组件
- • 使用 use 钩子
- • SWR 或 React Query
- • 传统的 useEffect
- • 支持实时更新
Pages Router 数据获取 (传统)
getStaticProps
构建时数据获取
getServerSideProps
请求时数据获取
getStaticPaths
动态路由预生成
// App Router 数据获取示例
// 1. 服务端组件 - 直接 async/await
export default async function PostsPage() {
// 并行获取数据
const [posts, categories] = await Promise.all([
fetch('https://api.example.com/posts').then(res => res.json()),
fetch('https://api.example.com/categories').then(res => res.json())
]);
return (
<div>
<h1>博客文章</h1>
<CategoryFilter categories={categories} />
<PostList posts={posts} />
</div>
);
}
// 2. 缓存配置
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'force-cache', // 强制缓存
// cache: 'no-store', // 不缓存
// next: { revalidate: 60 } // 60秒重新验证
});
return res.json();
}
// 3. 客户端组件数据获取
'use client';
import { use } from 'react';
function ClientPosts({ postsPromise }: { postsPromise: Promise<Post[]> }) {
const posts = use(postsPromise); // React 18+ use 钩子
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
// 4. 错误处理和加载状态
export default async function PostsPageWithErrorHandling() {
try {
const posts = await getPosts();
return <PostList posts={posts} />;
} catch (error) {
return <div>加载失败: {error.message}</div>;
}
}
API 调用机制与模式
比较 REST、GraphQL、tRPC、SWR 等不同的 API 调用模式和数据获取策略
REST API 演示
传统的 RESTful API 调用模式
服务器端代码:
// app/api/users/route.ts import { NextRequest, NextResponse } from 'next/server'; import { prisma } from '@/lib/prisma'; export async function GET(request: NextRequest) { try { const { searchParams } = new URL(request.url); const page = parseInt(searchParams.get('page') || '1'); const limit = parseInt(searchParams.get('limit') || '10'); const users = await prisma.user.findMany({ skip: (page - 1) * limit, take: limit, select: { id: true, name: true, email: true, createdAt: true } }); const total = await prisma.user.count(); return NextResponse.json({ users, pagination: { page, limit, total, pages: Math.ceil(total / limit) } }); } catch (error) { return NextResponse.json( { error: 'Internal Server Error' }, { status: 500 } ); } } export async function POST(request: NextRequest) { try { const body = await request.json(); const user = await prisma.user.create({ data: { name: body.name, email: body.email } }); return NextResponse.json(user, { status: 201 }); } catch (error) { return NextResponse.json( { error: 'Failed to create user' }, { status: 400 } ); } }
客户端代码:
// 客户端调用 async function fetchUsers(page = 1, limit = 10) { const response = await fetch(`/api/users?page=${page}&limit=${limit}`); if (!response.ok) { throw new Error('Failed to fetch users'); } return response.json(); } async function createUser(userData) { const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(userData) }); if (!response.ok) { throw new Error('Failed to create user'); } return response.json(); } // React 组件中使用 function UsersPage() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetchUsers() .then(data => { setUsers(data.users); setLoading(false); }) .catch(error => { console.error('Error:', error); setLoading(false); }); }, []); const handleCreateUser = async (userData) => { try { const newUser = await createUser(userData); setUsers(prev => [...prev, newUser]); } catch (error) { console.error('Create error:', error); } }; if (loading) return <div>Loading...</div>; return ( <div> {users.map(user => ( <div key={user.id}>{user.name}</div> ))} </div> ); }
API Routes (API 路由)
Next.js 允许你在同一个项目中创建 API 端点,实现全栈开发。 API Routes 让你可以轻松构建后端功能,而无需单独的服务器。
Pages Router API
文件位置: pages/api/
路由示例:
- •
pages/api/users.js
→/api/users
- •
pages/api/posts/[id].js
→/api/posts/:id
App Router API
文件位置: app/api/
路由示例:
- •
app/api/users/route.js
→/api/users
- •
app/api/posts/[id]/route.js
→/api/posts/:id
// App Router API Route 示例
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
// GET /api/users
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const page = parseInt(searchParams.get('page') || '1');
const limit = parseInt(searchParams.get('limit') || '10');
const users = await prisma.user.findMany({
skip: (page - 1) * limit,
take: limit,
select: {
id: true,
name: true,
email: true,
createdAt: true,
},
});
return NextResponse.json({
success: true,
data: users,
pagination: {
page,
limit,
total: await prisma.user.count(),
},
});
} catch (error) {
return NextResponse.json(
{ success: false, error: '获取用户失败' },
{ status: 500 }
);
}
}
// POST /api/users
export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { name, email } = body;
// 验证输入
if (!name || !email) {
return NextResponse.json(
{ success: false, error: '姓名和邮箱为必填项' },
{ status: 400 }
);
}
// 创建用户
const user = await prisma.user.create({
data: { name, email },
});
return NextResponse.json(
{ success: true, data: user },
{ status: 201 }
);
} catch (error) {
return NextResponse.json(
{ success: false, error: '创建用户失败' },
{ status: 500 }
);
}
}
// 动态路由示例
// app/api/users/[id]/route.ts
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
try {
const user = await prisma.user.findUnique({
where: { id: params.id },
include: {
posts: true,
_count: {
select: { posts: true, comments: true },
},
},
});
if (!user) {
return NextResponse.json(
{ success: false, error: '用户不存在' },
{ status: 404 }
);
}
return NextResponse.json({ success: true, data: user });
} catch (error) {
return NextResponse.json(
{ success: false, error: '获取用户失败' },
{ status: 500 }
);
}
}
// 中间件示例
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// API 路由的 CORS 处理
if (request.nextUrl.pathname.startsWith('/api/')) {
const response = NextResponse.next();
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return response;
}
return NextResponse.next();
}
export const config = {
matcher: '/api/:path*',
};
网络请求监控 & 性能分析
实时观察不同渲染模式下的网络请求瀑布图、性能指标和 Core Web Vitals
Next.js 内置优化演示
体验 Next.js 提供的各种性能优化功能,包括图片优化、代码分割、预取等
图片优化 演示
Next.js 内置优化详解
Next.js 提供了许多开箱即用的性能优化功能,让你的应用自动获得最佳性能, 而无需手动配置复杂的优化设置。
图片优化
- • 自动格式转换 (WebP, AVIF)
- • 响应式图片
- • 懒加载
- • 尺寸优化
代码分割
- • 自动页面分割
- • 动态导入
- • 共享代码提取
- • 按需加载
字体优化
- • 字体预加载
- • FOUT 防护
- • Google Fonts 优化
- • 子集化
脚本优化
- • 第三方脚本优化
- • 策略加载
- • 内联优化
- • 预连接
缓存策略
- • 自动静态优化
- • 数据缓存
- • 路由缓存
- • 组件缓存
预取和预加载
- • 链接预取
- • 路由预加载
- • 智能预测
- • 关键资源优先
// 1. 图片优化示例
import Image from 'next/image';
function OptimizedImage() {
return (
<Image
src="/hero.jpg"
alt="英雄图片"
width={800}
height={600}
priority // 优先加载
placeholder="blur" // 模糊占位符
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..." // 占位符数据
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" // 响应式尺寸
/>
);
}
// 2. 字体优化示例
import { Inter, Roboto_Mono } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // 字体交换策略
variable: '--font-inter', // CSS 变量
});
const robotoMono = Roboto_Mono({
subsets: ['latin'],
display: 'swap',
variable: '--font-roboto-mono',
});
export default function RootLayout({ children }) {
return (
<html lang="zh" className={`${inter.variable} ${robotoMono.variable}`}>
<body className="font-sans">{children}</body>
</html>
);
}
// 3. 脚本优化示例
import Script from 'next/script';
function Analytics() {
return (
<>
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="afterInteractive" // 页面交互后加载
/>
<Script id="google-analytics" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'GA_MEASUREMENT_ID');
`}
</Script>
</>
);
}
// 4. 动态导入和代码分割
import dynamic from 'next/dynamic';
import { Suspense } from 'react';
// 动态导入组件
const DynamicChart = dynamic(() => import('@/components/Chart'), {
loading: () => <p>加载图表中...</p>,
ssr: false, // 禁用服务端渲染
});
// 条件加载
const ConditionalComponent = dynamic(
() => import('@/components/HeavyComponent'),
{ ssr: false }
);
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<h1>仪表板</h1>
{showChart && (
<Suspense fallback={<div>加载中...</div>}>
<DynamicChart />
</Suspense>
)}
</div>
);
}
// 5. 链接预取
import Link from 'next/link';
function Navigation() {
return (
<nav>
<Link
href="/about"
prefetch={true} // 预取页面
>
关于我们
</Link>
<Link
href="/heavy-page"
prefetch={false} // 禁用预取
>
重型页面
</Link>
</nav>
);
}
流式渲染 (Streaming)
流式渲染是 Next.js 13+ 的强大特性,允许页面内容逐步加载,提供更好的用户体验。 用户可以立即看到页面的一部分,而其他部分仍在加载中。
// 流式渲染示例
import { Suspense } from 'react';
function SlowComponent() {
// 模拟慢组件
return <div>这是一个慢加载的组件</div>;
}
export default function StreamingPage() {
return (
<div>
<h1>页面标题 (立即显示)</h1>
<Suspense fallback={<div>加载中...</div>}>
<SlowComponent />
</Suspense>
<p>其他内容 (立即显示)</p>
</div>
);
}
在线代码编辑器
尝试修改下面的代码,体验 Next.js 15 的特性。代码将实时渲染在下方的预览区域:
Next.js 15 组件示例 - 可预览沙箱
Next.js 核心概念总结
路由系统
- App Router:支持嵌套布局
- 文件系统路由:直观易懂
- 动态路由:灵活的参数处理
- 特殊文件:loading、error、layout
渲染模式
- SSR:服务端渲染,SEO 友好
- SSG:静态生成,性能最佳
- ISR:增量静态再生
- CSR:客户端渲染,交互丰富
数据获取
- async/await:直接在组件中
- 自动缓存:智能缓存策略
- 并行获取:Promise.all 支持
- 错误处理:内置错误边界
API Routes
- 全栈开发:前后端一体化
- RESTful API:标准 HTTP 方法
- 中间件支持:请求拦截处理
- 类型安全:TypeScript 支持
性能优化
- 自动优化:开箱即用
- 代码分割:按需加载
- 图片优化:WebP、懒加载
- 字体优化:预加载、子集化
开发体验
- 热重载:快速开发反馈
- TypeScript:开箱即用
- ESLint:代码质量保证
- 零配置:快速上手
💡 Next.js 最佳实践建议
路由设计:合理规划页面结构,利用嵌套布局
渲染策略:根据内容特性选择合适的渲染模式
数据获取:在服务端获取数据,减少客户端请求
性能优化:使用 Next.js Image 组件优化图片
代码分割:使用动态导入进行组件懒加载
SEO 优化:合理使用 metadata API 提升搜索排名
🚀 快速访问演示页面
点击下方按钮直接访问不同渲染模式的演示页面,建议打开浏览器开发者工具的 Network 面板观察加载过程
💡 网络监控小贴士
访问演示页面时,建议按 F12 打开开发者工具, 切换到 Network 面板,然后刷新页面观察不同渲染模式的网络请求瀑布图和加载时序。 您会发现 SSG 加载最快,CSR 有更多 API 请求,SSR 在服务器端完成渲染等不同特征。
准备好了吗?
现在你已经了解了 Next.js 15 的基础概念,让我们继续学习 TypeScript 集成。
学习 TypeScript 集成