Next.js 15 新特性详解:App Router 完全指南
作者:君知博客团队
34 分钟阅读
#Next.js#React#App Router#服务端组件#性能优化
Next.js 15 新特性详解:App Router 完全指南
Next.js 15 带来了革命性的 App Router 架构,彻底改变了我们构建 React 应用的方式。本文将深入探讨 App Router 的核心概念和最佳实践。
什么是 App Router?
App Router 是 Next.js 13 引入并在 15 版本中完善的新路由系统。它基于 React Server Components,提供了更强大的功能和更好的性能。
主要特性
服务端组件(Server Components)
- 默认情况下,所有组件都是服务端组件
- 减少客户端 JavaScript 体积
- 直接访问后端资源(数据库、文件系统等)
客户端组件(Client Components)
- 使用
'use client'指令声明 - 支持交互和状态管理
- 可以使用 React Hooks
- 使用
嵌套布局(Nested Layouts)
- 支持多层嵌套的布局结构
- 布局在导航时保持状态
- 提升用户体验
服务端组件 vs 客户端组件
何时使用服务端组件?
// app/blog/page.tsx
// 默认是服务端组件,无需声明
import { getPosts } from '@/lib/posts'
export default async function BlogPage() {
// 可以直接在组件中进行数据获取
const posts = await getPosts()
return (
<div>
<h1>博客文章</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
)
}
适用场景:
- 数据获取
- 访问后端资源
- 保护敏感信息(API 密钥等)
- 减少客户端 JavaScript
何时使用客户端组件?
// components/Counter.tsx
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>计数:{count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>
</div>
)
}
适用场景:
- 交互功能(点击、输入等)
- 使用 React Hooks(useState、useEffect 等)
- 使用浏览器 API
- 使用第三方交互库
数据获取最佳实践
1. 服务端数据获取
// app/posts/[id]/page.tsx
async function getPost(id: string) {
const res = await fetch(`https://api.example.com/posts/${id}`, {
// Next.js 扩展的 fetch 选项
next: { revalidate: 3600 } // 每小时重新验证
})
return res.json()
}
export default async function PostPage({ params }: { params: { id: string } }) {
const post = await getPost(params.id)
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
)
}
2. 并行数据获取
// 同时获取多个数据源
async function getData() {
const [posts, categories, tags] = await Promise.all([
getPosts(),
getCategories(),
getTags(),
])
return { posts, categories, tags }
}
3. 流式渲染(Streaming)
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<h1>我的页面</h1>
{/* 立即显示,不等待数据 */}
<Suspense fallback={<div>加载中...</div>}>
<SlowComponent />
</Suspense>
</div>
)
}
路由和导航
1. 文件系统路由
app/
├── page.tsx # /
├── about/
│ └── page.tsx # /about
├── blog/
│ ├── page.tsx # /blog
│ └── [slug]/
│ └── page.tsx # /blog/:slug
└── api/
└── posts/
└── route.ts # /api/posts
2. 动态路由
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({
slug: post.slug,
}))
}
export default async function PostPage({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
return <article>{/* ... */}</article>
}
3. 编程式导航
'use client'
import { useRouter } from 'next/navigation'
export default function MyComponent() {
const router = useRouter()
return (
<button onClick={() => router.push('/blog')}>
前往博客
</button>
)
}
性能优化技巧
1. 图片优化
import Image from 'next/image'
export default function MyImage() {
return (
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // 优先加载
placeholder="blur" // 模糊占位符
/>
)
}
2. 字体优化
// app/layout.tsx
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({ children }) {
return (
<html lang="zh-CN" className={inter.className}>
<body>{children}</body>
</html>
)
}
3. 代码分割
// 动态导入,减少初始加载体积
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('@/components/HeavyComponent'), {
loading: () => <p>加载中...</p>,
ssr: false, // 禁用服务端渲染
})
SEO 优化
1. 元数据配置
// app/blog/[slug]/page.tsx
import type { Metadata } from 'next'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}): Promise<Metadata> {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.coverImage],
},
}
}
2. 结构化数据
export default function PostPage({ post }) {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: post.title,
datePublished: post.date,
author: {
'@type': 'Person',
name: post.author,
},
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<article>{/* ... */}</article>
</>
)
}
常见问题和解决方案
1. "use client" 边界问题
问题: 不知道在哪里添加 'use client'
解决方案:
- 从叶子组件开始添加
- 只在需要交互的组件中使用
- 尽可能保持服务端组件
2. 数据获取错误处理
// app/blog/[slug]/page.tsx
import { notFound } from 'next/navigation'
export default async function PostPage({ params }) {
const post = await getPost(params.slug)
if (!post) {
notFound() // 显示 404 页面
}
return <article>{/* ... */}</article>
}
3. 环境变量使用
// 服务端组件 - 可以使用所有环境变量
const apiKey = process.env.API_KEY
// 客户端组件 - 只能使用 NEXT_PUBLIC_ 前缀的变量
const publicKey = process.env.NEXT_PUBLIC_API_KEY
总结
Next.js 15 的 App Router 为我们提供了强大的工具来构建高性能的 React 应用。关键要点:
- 默认使用服务端组件,只在需要时使用客户端组件
- 充分利用流式渲染,提升用户体验
- 合理使用缓存策略,优化性能
- 重视 SEO 配置,提升搜索排名
通过掌握这些概念和最佳实践,你将能够构建出快速、可维护的 Next.js 应用。
相关资源
标签: #Next.js #React #AppRouter #服务端组件 #性能优化
发布日期: 2025-02-10
作者: 君知博客团队