Next.js路由系统
Next.js路由系统全面指南:从基础到高级的最新实现
Next.js作为React生态系统中最受欢迎的框架之一,其路由系统经历了从传统客户端路由到服务端路由的演进,最终形成了当前基于文件系统的两种路由系统:应用路由(App Router)和页面路由(Pages Router)。这两种路由系统各有特点,适用于不同场景,共同构成了Next.js强大的路由能力。本文将详细介绍Next.js最新路由系统的实现方式,从最基础的静态路由到复杂的动态路由、API路由和权限控制,每种情况都提供文件路径结构说明和代码示例,帮助开发者全面掌握Next.js路由的使用方法。
一、应用路由与页面路由系统概览
Next.js目前支持两种路由系统:应用路由(App Router)和页面路由(Pages Router)。这两种路由系统在设计理念、实现方式和适用场景上存在显著差异。
应用路由是Next.js 13版本引入的新路由系统,基于React 18的组件架构和服务器组件(Server Components)技术。它采用更直观的文件系统路由方式,通过目录层级自动映射路由路径,支持更灵活的布局管理和更高效的渲染策略。应用路由特别适合新项目开发,因为它能够充分利用React的新特性,提供更好的性能和更简洁的代码结构。
页面路由是Next.js的传统路由系统,基于React 17及之前的版本,通过在pages目录中创建文件来定义路由。它仍然被广泛使用,特别是对于已有项目维护。页面路由系统兼容性更好,适合需要与旧版React或第三方库集成的场景。
| 特性 | 应用路由 (App Router) | 页面路由 (Pages Router) | 
|---|---|---|
| 文件位置 | app/目录 | pages/目录 | 
| 核心组件 | page.tsx | 普通React组件 | 
| 布局组件 | layout.tsx | _app.js和_document.js | 
| 动态路由 | [param]和[...param] | [param]和[...param] | 
| 数据获取 | 直接在组件中使用fetch | 通过getServerSideProps或getStaticProps | 
| 路由导航 | useParams和useRouter | useRouter | 
| 中间件支持 | 原生支持 | 需要额外配置或第三方库 | 
二、静态路由:最基础的路由实现
静态路由是最简单的路由类型,直接对应固定URL路径。在两种路由系统中,实现方式有所不同。
应用路由中的静态路由
在应用路由系统中,静态路由通过在app/目录下创建包含page.tsx文件的目录来实现。文件名直接映射为路由路径。
文件路径结构:
app/page.tsx              // 对应根路径 /about/page.tsx            // 对应 /aboutblog/page.tsx            // 对应 /blog
 
组件示例:
// app/about/page.tsx
export default function AboutPage() {return (<div><h1>关于我们</h1><p>这是我们的公司介绍页面。</p></div>);
}
 
页面路由中的静态路由
在页面路由系统中,静态路由通过在pages/目录下创建普通JSX文件实现,文件名直接映射为路由路径。
文件路径结构:
pages/index.js               // 对应根路径 /about.js               // 对应 /aboutblog.js                 // 对应 /blog
 
组件示例:
// pages/about.js
export default function About() {return (<div><h1>关于我们</h1><p>这是我们的公司介绍页面。</p></div>);
}
 
特殊静态路由
两种路由系统都支持一些特殊静态路由,如根路径、404页面等。
应用路由的特殊静态路由:
// app/page.tsx - 根路径 /
export default function Home() {return <h1>欢迎来到首页</h1>;
}// app/notfound.tsx - 404页面
export default functionNotFound() {return (<div><h1>页面未找到</h1><p>您请求的页面不存在。</p></div>);
}
 
页面路由的特殊静态路由:
// pages/index.js - 根路径 /
export default function Home() {return <h1>欢迎来到首页</h1>;
}// pages/404.js - 404页面
export default functionNotFound() {return (<div><h1>页面未找到</h1><p>您请求的页面不存在。</p></div>);
}
 
三、动态路由:基于参数的路由匹配
动态路由允许创建基于参数的路由,根据URL中的参数渲染不同的内容。Next.js支持两种动态路由类型:命名参数路由和catch-all路由。
命名参数路由
命名参数路由使用方括号[param]定义,匹配固定数量的路径段。
应用路由中的命名参数路由
文件路径结构:
app/users/[id]/                // 动态参数 idpage.tsx          // 对应 /users/123
 
组件示例:
// app/users/[id]/page.tsx
import {路由器} from 'next/navigation';export default async function UserPage({ params }) {const { id } = await params; // Next.js 15+需用await解构const response = await fetch(`https://api.example.com/users/${id}`);const user = await response.json();// 参数验证if (!user) {return <div>用户不存在</div>;}return (<div><h1>{user.name}</h1><p>邮箱:{user.email}</p><p>角色:{user.role}</p></div>);
}
 
页面路由中的命名参数路由
文件路径结构:
pages/users/[id].js             // 动态参数 idindex.js            // 对应 /users
 
组件示例:
// pages/users/[id].js
import {路由器} from 'next/router';export default function UserPage() {const router =路由器();const { id } = router.query; // 通过query对象获取参数return (<div><h1>用户ID: {id}</h1><p>这是用户详情页面。</p></div>);
}// 服务端数据获取
export async function getServerSideProps(context) {const { id } = context.params; // 通过context.params获取参数const response = await fetch(`https://api.example.com/users/${id}`);const user = await response.json();// 参数验证if (!user) {return { notFound: true }; // 返回404页面}return {props: { user }, // 将数据传递给页面组件};
}
 
Catch-all路由
Catch-all路由使用[...param]语法定义,可以匹配任意数量的路径段。
应用路由中的Catch-all路由
文件路径结构:
app/docs/[...slug]/         // 动态参数 slugpage.tsx          // 对应 /docs/任意路径段
 
组件示例:
// app/docs/[...slug]/page.tsx
export default function DocsPage({ params }) {const { slug } = params; // slug是一个数组// 模拟从API获取文档数据const docData = await fetchDocData slug.join ( '/' );return (<div><h1>文档路径:{slug.join('/')}</h1><p>{docData.content}</p></div>);
}
 
页面路由中的Catch-all路由
文件路径结构:
pages/docs/[...slug].js       // 动态参数 slug
 
组件示例:
// pages/docs/[...slug].js
import {路由器} from 'next/router';export default function DocsPage() {const router =路由器();const { slug } = router.query; // slug是一个数组return (<div><h1>文档路径:{slug.join('/')}</h1><p>这是文档内容页面。</p></div>);
}
 
可选Catch-all路由
可选Catch-all路由使用双方括号[[...param]]语法定义,允许匹配根路径或任意数量的路径段。
应用路由中的可选Catch-all路由
文件路径结构:
app/posts/[[...slug]]/        // 可选动态参数 slugpage.tsx          // 对应 /posts 或 /posts/任意路径段
 
组件示例:
// app/posts/[[...slug]]/page.tsx
export default function PostsPage({ params }) {const { slug } = params || [];return (<div><h1>文章路径:{slug.length ? slug.join('/') : '最新文章'}</h1><p>这是文章列表页面。</p></div>);
}
 
页面路由中的可选Catch-all路由
文件路径结构:
pages/posts/[[...slug]].js      // 可选动态参数 slug
 
组件示例:
// pages/posts/[[...slug]].js
import {路由器} from 'next/router';export default function PostsPage() {const router =路由器();const { slug } = router.query; // slug可能为undefined或数组return (<div><h1>文章路径:{slug ? slug.join ( '/' ) : '最新文章'}</h1><p>这是文章列表页面。</p></div>);
}
 
四、API路由:创建服务器端API端点
Next.js允许开发者在前端项目中创建API路由,无需额外搭建Node.js服务器。两种路由系统都支持API路由,但实现方式有所不同。
应用路由中的API路由
在应用路由系统中,API路由通过在app/api/目录下创建route.js文件实现。
文件路径结构:
app/api/users/route.js          // 对应 /api/usersposts/[...slug]/        // 动态参数 slugroute.js        // 对应 /api/posts/任意路径段
 
组件示例:
// app/api/users/route.js
export async function GET(request) {const response = await fetch('https://api.example.com/users');const users = await response.json();return new Response(JSON.stringify(users), {headers: { 'Content-Type': 'application/json' },});
}// app/api/posts/[...slug]/route.js
export async function GET({ params }) {const { slug } = params; // slug是一个数组const path = slug.join ( '/' );const response = await fetch(`https://api.example.com/posts/${path}`);const post = await response.json();if (!post) {return new Response('Not Found', { status: 404 });}return new Response(JSON.stringify(post), {headers: { 'Content-Type': 'application/json' },});
}// 处理POST请求的示例
export async function POST(request) {const data = await request.json();// 数据处理逻辑...return new Response('Success', { status: 200 });
}
 
页面路由中的API路由
在页面路由系统中,API路由通过在pages/api/目录下创建普通JS文件实现。
文件路径结构:
pages/api/users.js            // 对应 /api/usersposts/[...slug].js      // 动态参数 slug,对应 /api/posts/任意路径段
 
组件示例:
// pages/api/users.js
export default function handler(req, res) {// 处理GET请求if (req.method === 'GET') {res.status(200).json({ message: '用户列表' });}// 处理POST请求else if (req.method === 'POST') {const { name, email } = req.body;// 创建用户逻辑...res.status(201).json({ message: '用户创建成功' });}// 不支持的请求方法else {res.status(405).json({ error: 'Method Not Allowed' });}
}
 
动态参数API路由示例:
// pages/api/posts/[...slug].js
export default function handler(req, res) {const { slug } = req.query; // slug是一个数组const path = slug.join ( '/' );// 根据路径获取文章...const post = await getPostByPath(path);if (!post) {res.status(404).json({ error: '文章不存在' });}res.status(200).json(post);
}
 
API路由的配置对象
页面路由系统允许通过config对象自定义API路由行为,如设置请求体大小限制等。
// pages/api/submit.js
export const config = {api: {// 设置请求体大小限制sizeLimit: '2mb',// 其他配置选项...}
};export default function handler(req, res) {// 处理请求逻辑...
}
 
五、路由嵌套与布局组件:构建一致的UI结构
路由嵌套和布局组件是Next.js路由系统的重要特性,它们允许开发者构建层次化的UI结构。
应用路由中的路由嵌套与布局组件
在应用路由系统中,路由嵌套通过目录层级实现,布局组件通过layout.tsx文件定义,支持多级嵌套。
文件路径结构:
app/layout.tsx           // 全局布局page.tsx             // 根页面blog/layout.tsx         // 博客局部布局page.tsx           // /blog页面[id]/               // 动态参数 idlayout.tsx       // 博客文章局部布局page.tsx         // /blog/123页面comments/layout.tsx     // 评论局部布局page.tsx       // /blog/123/comments页面
 
全局布局组件:
// app/layout.tsx
export default function RootLayout({ children }) {return (<html><head><title>我的博客</title><meta name="description" content="一个技术博客网站" /></head><body><Header /><main>{children}</main><Footer /></body></html>);
}
 
局部布局组件:
// app/blog/layout.tsx
export default function BlogLayout({ children }) {return (<div className="blog-container"><BlogHeader /><侧边栏 /><main>{children}</main></div>);
}
 
嵌套路由的渲染顺序:
 当访问/blog/123/comments时,Next.js会按以下顺序渲染布局组件:
- 全局布局(app/layout.tsx)
 - 博客布局(app/blog/layout.tsx)
 - 文章布局(app/blog/[id]/layout.tsx)
 - 评论布局(app/blog/[id]/comments/layout.tsx)
 - 评论页面(app/blog/[id]/comments/page.tsx)
 
页面路由中的路由嵌套与布局组件
在页面路由系统中,路由嵌套通过文件夹结构实现,但布局组件只能通过全局_app.js实现,无法支持局部嵌套布局。
文件路径结构:
pages/index.js             // 根路径 /about.js             // /aboutblog/index.js           // /blog[id].js            // /blog/123comments/index.js         // /blog/123/comments
 
全局布局组件:
// pages/_app.js
import React from 'react';
import App from 'next/app';
import Header from '../components common/Header';
import Footer from '../components common/Footer';export default function MyApp({ Component, pageProps }) {return (<div className="app-container"><Header /><Component {...pageProps} /><Footer /></div>);
}
 
手动实现局部布局:
 由于页面路由系统不支持局部布局组件,开发者通常通过高阶组件(HOC)或使用use wraps 钩子手动实现局部布局:
// components/blog Layout.js
import React from 'react';const BlogLayout = ({ children }) => {return (<div className="blog-container"><BlogHeader /><侧边栏 /><main>{children}</main></div>);
};export default BlogLayout;
 
// pages/blog/[id].js
import BlogLayout from '../components/blog Layout';const BlogPost = () => {return (<BlogLayout><div>这是博客文章页面</div></BlogLayout>);
};export default BlogPost;
 
六、路由导航:在应用中实现页面跳转
Next.js提供了多种方式实现路由导航,包括客户端组件和服务器组件中的导航。
应用路由中的路由导航
在应用路由系统中,路由导航主要通过next/navigation模块中的钩子实现。
使用Link组件导航:
// 客户端导航
import Link from 'next/navigation';function BlogList() {const posts = await getPosts(); // 服务端数据获取return (<ul>{posts.map((post) => (<li key={post.id}><Link href `/blog/${post.id}`}>{post.title}</Link></li>))}</ul>);
}
 
使用useRouter钩子编程式导航:
// 客户端导航
import {路由器} from 'next/navigation';function UserActions() {const router =路由器();const handleEdit = async () => {const { id } = await routerSearchParams; // 获取当前路由参数router.push `/users/${id}/edit`; // 跳转到编辑页面};return <button onClick={handleEdit}>编辑资料</button>;
}
 
服务端导航:
 在服务器组件中,可以直接使用Response对象进行重定向:
// app/dashboard/route.js
export async function GET() {const session = await checkSession();if (!session) {return NextResponse redirect('/login', { status: 307 });}// 返回正常响应...
}
 
页面路由中的路由导航
在页面路由系统中,路由导航主要通过next/router模块中的钩子实现。
使用Link组件导航:
// 客户端导航
import Link from 'next/router';function BlogList() {const posts = await getPosts(); // 服务端数据获取return (<ul>{posts.map((post) => (<li key={post.id}><Link href `/blog/${post.id}`}>{post.title}</Link></li>))}</ul>);
}
 
使用useRouter钩子编程式导航:
// 客户端导航
import {路由器} from 'next/router';function UserActions() {const router =路由器();const handleEdit = () => {const { id } = router.query; // 获取当前路由参数router.push `/users/${id}/edit`; // 跳转到编辑页面};return <button onClick={handleEdit}>编辑资料</button>;
}
 
服务端导航:
 在页面路由系统中,服务端导航通常通过返回redirect对象实现:
// pages/dashboard.js
export async function getServerSideProps() {const session = await checkSession();if (!session) {return {redirect: {destination: '/login',permanent: false,},};}return {props: {}, // 传递给页面组件的props};
}
 
七、路由权限控制:保护敏感页面
路由权限控制是确保敏感页面只能被授权用户访问的重要机制。Next.js提供了多种实现方式。
应用路由中的权限控制
在应用路由系统中,权限控制主要通过中间件实现,这是一种高效且集中式的解决方案。
中间件实现权限控制:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';const protectedRoutes = ['/dashboard','/profile','/admin',
];
const publicRoutes = ['/','/login','/signup',
];export default async function authMiddleware(request: NextRequest
) {const path = request.nextUrl.pathname;const isProtected = protectedRoutes.some((route) => path.startsWith(route));const isPublic = publicRoutes.includes(path);// 获取用户会话信息const session = await checkSession(request);// 保护路由检查if (isProtected && !session userId ) {return NextResponse redirect(new URL('/login', request.nextUrl),{ status: 307 });}// 公共路由检查if (isPublic && session userId ) {return NextResponse redirect(new URL('/dashboard', request.nextUrl),{ status: 307 });}return NextResponse.next();
}export const config = {matcher: '/((?!api|_next|.*\\.(?:css|js|png|jpg|svg)).*)',
};
 
在路由组件中检查权限:
// app/dashboard/page.tsx
import {路由器} from 'next/navigation';export default async function DashboardPage() {const router =路由器();// 获取用户会话信息const session = await checkSession();// 未登录用户重定向if (!session userId ) {router.push('/login');return null; // 阻止渲染}// 获取仪表板数据...const data = await getDashboardData();return (<div><h1>仪表板</h1><p>这是您的个人仪表板。</p><pre>{JSON.stringify(data, null, 2)}</pre></div>);
}
 
页面路由中的权限控制
在页面路由系统中,权限控制可以通过服务端获取函数或客户端组件实现。
使用getServerSideProps检查权限:
// pages/dashboard.js
export async function getServerSideProps(context) {const session = await checkSession(context);if (!session userId ) {return {redirect: {destination: '/login',permanent: false,},};}// 获取仪表板数据...const data = await getDashboardData();return {props: { data }, // 传递给页面组件的props};
}export default function Dashboard({ data }) {return (<div><h1>仪表板</h1><p>这是您的个人仪表板。</p><pre>{JSON.stringify(data, null, 2)}</pre></div>);
}
 
使用客户端组件检查权限:
// pages/dashboard.js
进口' use client 'import {路由器} from 'next/router';
import { useEffect } from 'react';export default function Dashboard() {const router =路由器();useEffect(() => {const checkAuth = async () => {const session = await checkSession();if (!session userId ) {router.push('/login');}};checkAuth();}, []);return (<div><h1>仪表板</h1><p>这是您的个人仪表板。</p></div>);
}
 
八、路由重定向:处理URL变更
路由重定向是处理URL变更的重要机制,Next.js提供了多种实现方式。
应用路由中的重定向
在应用路由系统中,重定向可以通过redirect()函数或中间件实现。
服务端组件中的重定向:
// app/dashboard/page.tsx
import { redirect } from 'next/navigation';export default async function DashboardPage() {const session = await checkSession();if (!session userId ) {redirect('/login'); // 307临时重定向}return <div>仪表板内容</div>;
}
 
中间件中的重定向:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';export default function oldUrlRedirect(request: NextRequest) {const url = request.nextUrl.pathname;// 将/old-path重定向到/new-pathif (url === '/old-path') {return NextResponse redirect('/new-path', {status: 308, // 308永久重定向});}return NextResponse.next();
}export const config = {matcher: '/old-path',
};
 
页面路由中的重定向
在页面路由系统中,重定向可以通过服务端获取函数或客户端组件实现。
服务端获取函数中的重定向:
// pages/dashboard.js
export async function getServerSideProps(context) {const session = await checkSession(context);if (!session userId ) {return {redirect: {destination: '/login',permanent: false,},};}return {props: {}, // 传递给页面组件的props};
}
 
客户端组件中的重定向:
// pages/dashboard.js
进口' use client 'import {路由器} from 'next/router';
import { useEffect } from 'react';export default function Dashboard() {const router =路由器();useEffect(() => {const checkAuth = async () => {const session = await checkSession();if (!session userId ) {router.push('/login');}};checkAuth();}, []);return <div>仪表板内容</div>;
}
 
九、路由优化:提升应用性能
Next.js提供了多种路由优化技术,帮助开发者提升应用性能。
应用路由中的路由优化
在应用路由系统中,路由优化主要通过并行数据获取、流式渲染和增量静态再生成(ISR)实现。
并行数据获取:
// app/dashboard/page.tsx
export default async function DashboardPage() {const [user, posts] = await Promise.all([fetch('https://api.example.com/user'),fetch('https://api.example.com/posts'),]);return (<div><h1>仪表板</h1><p>欢迎,{user.name}</p><h2>最新帖子</h2><ul>{posts.map((post) => <li key={post.id}>{post.title}</li>)}</ul></div>);
}
 
流式渲染:
// app/blog/[id]/page.tsx
import {路由器} from 'next/navigation';export default async function BlogPost({ params }) {const { id } = await params;// 先返回部分HTML内容const initialContent = (<div><h1>加载中...</h1><p>正在获取文章内容...</p></div>);// 在后台获取数据const response = await fetch(`https://api.example.com/blog/${id}`);const post = await response.json();// 返回完整内容return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /></div>);
}
 
增量静态再生成(ISR):
// app/blog/[id]/page.tsx
import {路由器} from 'next/navigation';export default async function BlogPost({ params }) {const { id } = await params;// 获取文章内容const post = await getPost(id);return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /></div>);
}// 启用ISR,每30分钟更新一次
export const revalidate = 30; // 秒
 
页面路由中的路由优化
在页面路由系统中,路由优化主要通过静态生成(SSG)、服务器端渲染(SSR)和预加载(Prefetching)实现。
静态生成(SSG):
// pages/blog/[id].js
export async function getStaticProps({ params }) {const id = params.id;// 获取文章内容const post = await getPost(id);return {props: { post }, // 传递给页面组件的propsrevalidate: 30, // 启用ISR,每30秒更新一次};
}export async function getStaticPaths() {// 获取所有可能的文章IDconst posts = await getPosts();// 生成静态路径const paths = posts.map((post) => ({params: { id: post.id.toString() },}));// 允许生成未预先渲染的路径return {paths,fallback: true,};
}
 
服务器端渲染(SSR):
// pages/dashboard.js
export async function getServerSideProps(context) {const session = await checkSession(context);if (!session userId ) {return {redirect: {destination: '/login',permanent: false,},};}// 获取仪表板数据...const data = await getDashboardData();return {props: { data }, // 传递给页面组件的props};
}
 
预加载(Prefetching):
// pages/blog.js
import Link from 'next/router';function BlogList() {// 预加载链接const links = posts.map((post) => (<Link href `/blog/${post.id}`} key={post.id}>{post.title}</Link>));return <div>{links}</div>;
}
 
十、路由与SEO:提高搜索引擎排名
路由设计对SEO有重要影响,Next.js提供了多种优化SEO的路由策略。
应用路由与SEO
应用路由系统默认支持服务端渲染(SSR),这对SEO非常友好。通过合理设计路由结构和使用ISR,开发者可以构建既高性能又SEO友好的应用。
服务端渲染(SSR)示例:
// app/blog/[id]/page.tsx
export default async function BlogPost({ params }) {const { id } = await params;// 获取文章内容const post = await getPost(id);return (<html><head><title>{post.title}</title><meta name="description" content={post摘要} /></head><body><文章标题={post.title}内容={post.content} /></body></html>);
}
 
静态生成(SSG)示例:
// app/blog/[id]/page.tsx
export default function BlogPost({ post }) {return (<html><head><title>{post.title}</title><meta name="description" content={post摘要} /></head><body><文章标题={post.title}内容={post.content} /></body></html>);
}export async function generateStaticParams() {// 获取所有可能的文章IDconst posts = await getPosts();// 生成静态路径return posts.map((post) => ({params: { id: post.id.toString() },}));
}
 
页面路由与SEO
页面路由系统也支持多种SEO优化策略,但需要开发者手动实现。
服务端渲染(SSR)示例:
// pages/blog/[id].js
export async function getServerSideProps(context) {const id = context.params.id;// 获取文章内容const post = await getPost(id);return {props: { post }, // 传递给页面组件的props};
}export default function BlogPost({ post }) {return (<html><head><title>{post.title}</title><meta name="description" content={post摘要} /></head><body><文章标题={post.title}内容={post.content} /></body></html>);
}
 
静态生成(SSG)示例:
// pages/blog/[id].js
export async function getStaticProps({ params }) {const id = params.id;// 获取文章内容const post = await getPost(id);return {props: { post }, // 传递给页面组件的props};
}export async function getStaticPaths() {// 获取所有可能的文章IDconst posts = await getPosts();// 生成静态路径return {paths: posts.map((post) => ({params: { id: post.id.toString() },})),fallback: false, // 禁用客户端回退};
}export default function BlogPost({ post }) {return (<html><head><title>{post.title}</title><meta name="description" content={post摘要} /></head><body><文章标题={post.title}内容={post.content} /></body></html>);
}
 
十一、路由与国际化:支持多语言应用
Next.js提供了强大的国际化支持,允许开发者构建支持多语言的应用。
应用路由中的国际化
在应用路由系统中,国际化通过next-intl等第三方库实现,支持动态语言切换和本地化路由。
国际化配置示例:
// app/i18n.js
import { createIntlServer } from 'next-intl/server';export async function阻力() {const { locale } = router.query;// 创建国际化服务器const server = await createIntlServer({defaultLocale: 'en',locales: ['en', 'zh'],messages: {en: await import ('./messages/en.json'),zh: await import ('./messages/zh.json'),},});return {props: { server },};
}
 
多语言路由示例:
// app/[locale]/blog/[id]/page.tsx
import {路由器} from 'next/navigation';export default async function BlogPost({ params }) {const { locale, id } = await params;// 获取文章内容const post = await getPost(id, locale);return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /></div>);
}
 
页面路由中的国际化
在页面路由系统中,国际化通过next-i18next等第三方库实现,同样支持多语言应用。
国际化配置示例:
// next-i18next.config.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';import en from './messages/en.json';
import zh from './messages/zh.json';const resources = {en: { translation: en },zh: { translation: zh },
};const i18nConfig = {resources,detection: { order: ['navigator'] },interpolation: { escapeValue: false },fallbackLng: 'en',initialI18nStore: true,use: [initReactI18next],
};export default i18n.use(i18nConfig);
 
多语言路由示例:
// pages/[locale]/blog/[id].js
import {路由器} from 'next/router';
import i18n from '../next-i18next.config';export default function BlogPost() {const router =路由器();const { locale, id } = router.query;// 获取文章内容const post = await getPost(id, locale);return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /></div>);
}
 
十二、路由与状态管理:维护应用状态
路由与状态管理是构建复杂应用的关键,Next.js提供了多种方式实现路由状态管理。
应用路由中的状态管理
在应用路由系统中,状态管理可以通过React Context API、Zustand或Recoil等库实现,与路由系统无缝集成。
使用Context API管理路由状态:
// app context/ navigation. tsx
import {路由器} from 'next/navigation';
import { createContext, useState } from 'react';export const NavigationContext = createContext(null);export const NavigationProvider = ({ children }) => {const router =路由器();const [currentRoute, setCurrentRoute] = useState(router.pathname);// 监听路由变化useEffect(() => {const handleRouteChange = (url) => {setCurrentRoute(url);};router events. on('routeChangeComplete', handleRouteChange);return () => {router events. off('routeChangeComplete', handleRouteChange);};}, []);return (<NavigationContext.Provider value={{ currentRoute }}>{children}</NavigationContext.Provider>);
};
 
在布局组件中使用状态:
// app/layout.tsx
import {路由器} from 'next/navigation';
import { NavigationContext } from './context/navigation';export default function RootLayout({ children }) {const { currentRoute } = React . useContext (NavigationContext);return (<html><head><title>我的博客 - {currentRoute}</title></head><body><Header currentRoute={currentRoute} /><main>{children}</main><Footer /></body></html>);
}
 
页面路由中的状态管理
在页面路由系统中,状态管理同样可以通过Context API或状态管理库实现,但需要额外配置。
使用Context API管理路由状态:
// pages/_app.js
import React from 'react';
import App from 'next/app';
import {路由器} from 'next/router';
import { createContext, useState } from 'react';export const NavigationContext = createContext(null);export default function MyApp({ Component, pageProps }) {const [currentRoute, setCurrentRoute] = useState('/');// 监听路由变化useEffect(() => {const handleRouteChange = (url) => {setCurrentRoute(url);};路由器. onRouteChangeComplete(handleRouteChange);return () => {路由器. offRouteChangeComplete(handleRouteChange);};}, []);return (<NavigationContext.Provider value={{ currentRoute }}><div className="app-container"><Header currentRoute={currentRoute} /><Component {...pageProps} /><Footer /></div></NavigationContext.Provider>);
}
 
十三、路由与数据获取:结合API与页面数据
路由与数据获取的结合是构建数据驱动应用的关键,Next.js提供了多种方式实现路由相关的数据获取。
应用路由中的数据获取
在应用路由系统中,数据获取可以直接在组件中使用fetch函数,无需额外导出函数。
服务端数据获取:
// app/blog/[id]/page.tsx
export default async function BlogPost({ params }) {const { id } = await params;// 获取文章内容const postResponse = await fetch(`https://api.example.com/blog/${id}`);const post = await postResponse.json();// 获取评论const commentsResponse = await fetch(`https://api.example.com/blog/${id}/comments`);const comments = await commentsResponse.json();return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /><h2>评论</h2><ul>{comments.map((comment) => (<li key={comment.id}>{comment.content}</li>))}</ul></div>);
}
 
并行数据获取:
// app/dashboard/page.tsx
export default async function DashboardPage() {const [user, posts] = await Promise.all([fetch('https://api.example.com/user'),fetch('https://api.example.com/posts'),]);return (<div><h1>仪表板</h1><p>欢迎,{user.name}</p><h2>最新帖子</h2><ul>{posts.map((post) => <li key={post.id}>{post.title}</li>)}</ul></div>);
}
 
页面路由中的数据获取
在页面路由系统中,数据获取主要通过getServerSideProps或getStaticProps实现。
服务端数据获取:
// pages/blog/[id].js
export async function getServerSideProps(context) {const id = context.params.id;// 获取文章内容const postResponse = await fetch(`https://api.example.com/blog/${id}`);const post = await postResponse.json();// 获取评论const commentsResponse = await fetch(`https://api.example.com/blog/${id}/comments`);const comments = await commentsResponse.json();return {props: { post, comments }, // 传递给页面组件的props};
}export default function BlogPost({ post, comments }) {return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /><h2>评论</h2><ul>{comments.map((comment) => (<li key={comment.id}>{comment.content}</li>))}</ul></div>);
}
 
静态生成数据获取:
// pages/blog/[id].js
export async function getStaticProps({ params }) {const id = params.id;// 获取文章内容const postResponse = await fetch(`https://api.example.com/blog/${id}`);const post = await postResponse.json();// 获取评论const commentsResponse = await fetch(`https://api.example.com/blog/${id}/comments`);const comments = await commentsResponse.json();return {props: { post, comments }, // 传递给页面组件的propsrevalidate: 60, // 启用ISR,每60秒更新一次};
}export async function getStaticPaths() {// 获取所有可能的文章IDconst posts = await getPosts();// 生成静态路径return {paths: posts.map((post) => ({params: { id: post.id.toString() },})),fallback: true, // 允许客户端回退};
}export default function BlogPost({ post, comments }) {return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /><h2>评论</h2><ul>{comments.map((comment) => (<li key={comment.id}>{comment.content}</li>))}</ul></div>);
}
 
十四、路由与边缘计算:构建全球分布式应用
Next.js 14+版本引入了边缘计算支持,允许开发者将路由逻辑部署到全球边缘节点,提供更快速的访问体验。
应用路由中的边缘计算
在应用路由系统中,边缘计算通过在路由组件中使用fetch函数并设置next羰基物来实现。
边缘计算路由示例:
// app/blog/[id]/page.tsx
import {路由器} from 'next/navigation';export default async function BlogPost({ params }) {const { id } = await params;// 使用边缘计算获取数据const post = await fetch(`https://api.example.com/blog/${id}`, {next: { revalidate: 60 }, // 缓存60秒});return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /></div>);
}
 
页面路由中的边缘计算
在页面路由系统中,边缘计算同样通过fetch函数的next羰基物实现,但需要在服务端获取函数中使用。
边缘计算路由示例:
// pages/blog/[id].js
export async function getServerSideProps(context) {const id = context.params.id;// 使用边缘计算获取数据const postResponse = await fetch(`https://api.example.com/blog/${id}`, {next: { revalidate: 60 }, // 缓存60秒});const post = await postResponse.json();return {props: { post }, // 传递给页面组件的props};
}export default function BlogPost({ post }) {return (<div><h1>{post.title}</h1><p>作者:{post.author}</p><div危及={post.content} /></div>);
}
 
十五、路由与错误处理:优雅应对异常情况
路由与错误处理是构建健壮应用的关键,Next.js提供了多种机制处理路由相关的错误。
应用路由中的错误处理
在应用路由系统中,错误处理主要通过error.tsx组件和中间件实现。
全局错误处理:
// app/error.tsx
import {路由器} from 'next/navigation';export default function ErrorPage() {const { error } =路由器();return (<div><h1>错误</h1><p>{error.message}</p><button onClick={() =>路由器.push('/')}>返回首页</button></div>);
}
 
局部错误处理:
// app/blog/[id]/error.tsx
import {路由器} from 'next/navigation';export default function BlogErrorPage() {const { error } =路由器();return (<div><h1>博客文章错误</h1><p>{error.message}</p><button onClick={() =>路由器.push('/blog')}>返回博客列表</button></div>);
}
 
中间件错误处理:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';export default function errorHandlingMiddleware(request: NextRequest
) {try {// 执行可能出错的逻辑...} catch (error) {return NextResponse redirect('/error', {status: 307,});}return NextResponse.next();
}
 
页面路由中的错误处理
在页面路由系统中,错误处理主要通过_error.js组件实现,但需要手动处理特定错误。
全局错误处理:
// pages/_error.js
import {路由器} from 'next/router';export default function ErrorPage({ res }) {const { err } =路由器();return (<div><h1>错误</h1><p>{err.message}</p><button onClick={() =>路由器.push('/')}>返回首页</button></div>);
}
 
十六、路由与性能监控:优化应用性能
路由与性能监控是确保应用高性能的关键,Next.js提供了多种工具和机制监控路由性能。
应用路由中的性能监控
在应用路由系统中,性能监控可以通过Next.js内置的性能分析工具和自定义中间件实现。
使用内置性能分析工具:
// 在next.config.js中启用性能分析
module.exports = {experimental: {appDir: true,},future: {strictRouterState: true,},trailingSlashes: true,swcMinify: true,// 启用性能分析analyze: true,
};
 
自定义中间件监控路由性能:
// app/middleware.js
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';export default function performanceMiddleware(request: NextRequest
) {const start = Date . now();return NextResponse.next(). finally(() => {const duration = Date . now() - start;console.log(`路由 ${request.nextUrl.pathname} 耗时 ${duration}ms`);});
}
 
页面路由中的性能监控
在页面路由系统中,性能监控可以通过Next.js内置的性能分析工具和自定义代码实现。
使用内置性能分析工具:
// 在next.config.js中启用性能分析
module.exports = {// 启用性能分析analyze: true,// 其他配置...
};
 
自定义代码监控路由性能:
// pages/_app.js
import React, { useEffect } from 'react';
import App from 'next/app';
import {路由器} from 'next/router';export default function MyApp({ Component, pageProps }) {useEffect(() => {const handleRouteChange = (url) => {console.log(`路由变更:${url},时间:${new Date().toISOString()}`);};路由器. onRouteChangeComplete(handleRouteChange);return () => {路由器. offRouteChangeComplete(handleRouteChange);};}, []);return (<div className="app-container"><Component {...pageProps} /></div>);
}
 
十七、路由与第三方库集成:扩展路由功能
路由与第三方库集成是构建复杂应用的重要能力,Next.js支持多种第三方路由库和工具。
应用路由与第三方库集成
在应用路由系统中,可以集成next connect等库增强API路由功能。
使用next connect增强API路由:
// app/api/users/route.js
import { NextResponse } from 'next/server';
import { NextConnect } from 'next connect';const connect = NextConnect();connect.use((req, res, next) => {console.log('请求到达:', req.nextUrl.pathname);next();
});connect.get(async (req, res) => {const users = await fetchUsers();return NextResponse.json(users);
});connect.post(async (req, res) => {const data = await req.json();// 创建用户逻辑...return NextResponse.json({ message: '用户创建成功' });
});export default connect;
 
页面路由与第三方库集成
在页面路由系统中,可以集成react router dom等库实现更复杂的路由逻辑。
使用react router dom增强路由功能:
// pages/_app.js
import React from 'react';
import App from 'next/app';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';const history = createBrowserHistory();export default class MyApp extends App {render() {const { Component, pageProps } = this.props;return (<Router history={history}><Component {...pageProps} /></Router>);}
}
 
十八、路由与测试:确保路由功能正确性
路由与测试是确保应用质量的重要环节,Next.js提供了多种测试路由功能的方法。
应用路由测试
在应用路由系统中,可以使用next testing library和react testing library测试路由功能。
测试动态路由组件:
// tests/blog Post Test. tsx
import {渲染,屏幕} from '@testing library / react';
import {路由器} from 'next/navigation';// 模拟路由器
const mockRouter = {push: vi.fn(),replace: vi.fn(),query: { id: '123' },
};vi.mock('next/navigation', () => ({路由器: () => mockRouter,useParams: () => ({ id: '123' }),redirect: vi.fn(),permanentRedirect: vi.fn(),
}));describe('博客文章页面', () => {it('渲染文章标题和内容', async () => {// 模拟获取文章数据vi mock.('fetch', vi.fn().mockImplementationOnce(() =>Promise.resolve({json: () => Promise.resolve({ title: '测试文章', content: '测试内容' }),})));渲染(<BlogPostPage params={{ id: '123' }} />);expect await (screen.findByText('测试文章')).toBeInTheDocument();expect await (screen.findByText('测试内容')).toBeInTheDocument();});it('处理文章不存在的情况', async () => {// 模拟获取不存在的文章vi mock.('fetch', vi.fn().mockImplementationOnce(() =>Promise.resolve({json: () => Promise.resolve(null),})));渲染(<BlogPostPage params={{ id: '123' }} />);expect await (screen.findByText('文章不存在')).toBeInTheDocument();});
});
 
页面路由测试
在页面路由系统中,同样可以使用next testing library和react testing library测试路由功能。
测试动态路由组件:
// tests/blog Post Test. tsx
import {渲染,screen} from '@testing library / react';
import {路由器} from 'next/router';// 模拟路由器
const mockRouter = {push: vi.fn(),replace: vi.fn(),query: { id: '123' },
};vi.mock('next/router', () => ({路由器: () => mockRouter,useEffect: vi.fn(),useLayoutEffect: vi.fn(),Link: vi.fn().mockImplementation(({ href, children }) => (<a href={href}>{children}</a>)),// 模拟getServerSidePropsgetServerSideProps: vi.fn().mockImplementationOnce(() =>Promise.resolve({props: { post: { title: '测试文章', content: '测试内容' } },})),
}));describe('博客文章页面', () => {it('渲染文章标题和内容', () => {渲染(<BlogPostPage post={{ title: '测试文章', content: '测试内容' }} />);expect(screen.findByText('测试文章')).toBeInTheDocument();expect(screen.findByText('测试内容')).toBeInTheDocument();});it('处理文章不存在的情况', () => {// 模拟getServerSideProps返回notFoundvi mock.('getServerSideProps', vi.fn().mockImplementationOnce(() =>Promise.resolve({ notFound: true })));渲染(<BlogPostPage post={{}} />);expect(screen.findByText('页面未找到')).toBeInTheDocument();});
});
 
十九、路由与部署:将应用部署到生产环境
路由与部署是将开发完成的应用推向用户的关键环节,Next.js提供了多种部署选项和优化策略。
应用路由部署
应用路由系统与Next.js的部署流程兼容,可以部署到Vercel、AWS、Google Cloud等平台。
部署到Vercel:
// vercel.json
{"version": 2,"builds": [{"src": "package.json","use": "@next刀片"}],"routes": [{"src": "/((?!_next/|api/).*)","dest": "/app/index.js"},{"src": "/api/(.*)","dest": "/pages/api/index.js"}]
}
 
页面路由部署
页面路由系统同样支持多种部署选项,但需要注意一些特定配置。
部署到AWS:
// next.config.js
module.exports = {experimental: {appDir: true,},trailingSlashes: true,swcMinify: true,// AWS部署配置assetPrefix: process.env.NEXT Public Prefix,distDir: 'dist',generateBuildId: async () => 'build-' + Date . now().toString(),
};
 
二十、路由与版本控制:管理路由变更
路由与版本控制是管理应用演进的重要能力,Next.js提供了多种路由版本控制策略。
应用路由版本控制
在应用路由系统中,可以通过目录层级实现路由版本控制。
版本控制路由结构:
app/api/v1/users/route.js       // /api/v1/usersv2/users/route.js       // /api/v2/users
 
页面路由版本控制
在页面路由系统中,可以通过路径命名实现路由版本控制。
版本控制路由结构:
pages/api/v1/users.js         // /api/v1/usersv2/users.js         // /api/v2/users
 
二十一、路由与微前端:构建模块化应用
路由与微前端是构建大型模块化应用的重要策略,Next.js支持通过路由系统集成微前端组件。
应用路由微前端集成
在应用路由系统中,可以通过目录层级和动态路由实现微前端集成。
微前端路由结构:
app/micro/[module]/           // 动态模块参数[component]/      // 动态组件参数page.tsx        // 对应 /micro/module/component
 
微前端组件示例:
// app/micro/[module]/[component]/page.tsx
import {路由器} from 'next/navigation';export default async function MicroFrontendPage({ params }) {const { module, component } = await params;// 动态加载微前端组件const MicroComponent = await import(`./micro/${module}/${component}.tsx`);return <MicroComponent default />;
}
 
页面路由微前端集成
在页面路由系统中,可以通过路径命名和组件动态加载实现微前端集成。
微前端路由结构:
pages/micro/[module]/           // 动态模块参数[component]/      // 动态组件参数index.js        // 对应 /micro/module/component
 
微前端组件示例:
// pages/micro/[module]/[component]/index.js
import {路由器} from 'next/router';export default function MicroFrontendPage() {const router =路由器();const { module, component } = router.query;// 动态加载微前端组件const MicroComponent = require(`./micro/${module}/${component}.jsx`). default;return <MicroComponent />;
}
 
二十二、路由与渐进式Web应用(PWA):构建离线体验
路由与PWA是提供离线体验和推送通知的重要策略,Next.js支持通过路由系统构建PWA应用。
应用路由PWA集成
在应用路由系统中,可以通过next/pwa和workbox构建PWA应用。
PWA配置示例:
// next.config.js
module.exports = {experimental: {appDir: true,},future: {strictRouterState: true,},trailingSlashes: true,swcMinify: true,// PWA配置pwa: {dest: 'public',register: true,skipWait: true,updateOnLoad: true,},// 其他配置...
};
 
Service Worker路由缓存:
// public/service worker.js
import { workbox } from 'workbox';// 缓存静态路由
workbox. strategies. cacheFirst({cacheName: 'static-routes',maxAgeSeconds: 30 * 24 * 60 * 60, // 30天strategy: 'CacheFirst',
});// 缓存动态路由
workbox. strategies. cacheWith策展人({cacheName: 'dynamic-routes',maxStaleSeconds: 60 * 60, // 1小时strategy: 'CacheWith策展人',
});
 
页面路由PWA集成
在页面路由系统中,同样可以通过next/pwa和workbox构建PWA应用。
PWA配置示例:
// next.config.js
module.exports = {// PWA配置pwa: {dest: 'public',register: true,skipWait: true,updateOnLoad: true,},// 其他配置...
};
 
Service Worker路由缓存:
// public/service worker.js
import { workbox } from 'workbox';// 缓存静态路由
workbox. strategies. cacheFirst({cacheName: 'static-routes',maxAgeSeconds: 30 * 24 * 60 * 0, // 30天strategy: 'CacheFirst',
});// 缓存动态路由
workbox. strategies. cacheWith策展人({cacheName: 'dynamic-routes',maxStaleSeconds: 60 * 60, // 1小时strategy: 'CacheWith策展人',
});
 
二十三、路由与边缘缓存:提升全球访问速度
路由与边缘缓存是提升全球用户访问速度的重要策略,Next.js支持通过路由系统实现边缘缓存。
应用路由边缘缓存
在应用路由系统中,可以通过next羰基物和边缘计算实现路由边缘缓存。
边缘缓存路由示例:
// app/blog/[id]/route.js
export async function GET({ params }) {const { id } = params;// 获取文章内容const post = await fetch(`https://api.example.com/blog/${id}`, {next: { revalidate: 60 }, // 每60秒重新验证一次});return new Response post. json(), {headers: { 'Content-Type': 'application/json' },};
}
 
页面路由边缘缓存
在页面路由系统中,可以通过getServerSideProps和边缘计算实现路由边缘缓存。
边缘缓存路由示例:
// pages/blog/[id].js
export async function getServerSideProps(context) {const id = context.params.id;// 获取文章内容const post = await fetch(`https://api.example.com/blog/${id}`, {next: { revalidate: 60 }, // 每60秒重新验证一次});return {props: { post }, // 传递给页面组件的props};
}
 
二十四、路由与身份验证:保护用户数据
路由与身份验证是保护用户数据和实现用户权限管理的重要机制,Next.js支持通过路由系统实现身份验证。
应用路由身份验证
在应用路由系统中,身份验证可以通过中间件和服务器组件实现。
身份验证中间件:
// app/middleware.js
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';export default function authMiddleware(request: NextRequest) {// 检查认证状态const session = request cookies. get('session');if (!session) {return NextResponse redirect('/login', {status: 307,});}return NextResponse.next();
}
 
受保护路由组件:
// app/dashboard/page.tsx
import {路由器} from 'next/navigation';export default async function DashboardPage() {const session = await checkSession();if (!session userId ) {redirect('/login');}// 获取仪表板数据...const data = await getDashboardData();return (<div><h1>仪表板</h1><p>欢迎,{session userId }</p><pre>{JSON.stringify(data, null, 2)}</pre></div>);
}
 
页面路由身份验证
在页面路由系统中,身份验证可以通过服务端获取函数和客户端组件实现。
服务端身份验证:
// pages/dashboard.js
export async function getServerSideProps(context) {const session = await checkSession(context);if (!session userId ) {return {redirect: {destination: '/login',permanent: false,},};}// 获取仪表板数据...const data = await getDashboardData();return {props: { data }, // 传递给页面组件的props};
}
 
客户端身份验证:
// pages/dashboard.js
进口' use client 'import {路由器} from 'next/router';
import { useEffect } from 'react';export default function Dashboard() {const router =路由器();useEffect(() => {const checkAuth = async () => {const session = await checkSession();if (!session userId ) {r