重定向(Redirecting)
redirect 函数
redirect函数允许您将用户重定向到另一个URL。您可以在服务器组件、路由处理程序和服务器操作中调用redirect。
redirect通常在事件之后使用。例如,创建帖子:
'use server'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
export async function createPost(id: string) {
try {
// Call database
} catch (error) {
// Handle errors
}
revalidatePath('/posts') // Update cached posts
redirect(`/post/${id}`) // Navigate to the new post page
}
注意事项
redirect默认返回307(临时重定向)状态码。在服务器操作中使用时,它会返回303(请参阅其他),这通常用于在POST请求的结果下重定向到成功页面。
redirect在内部抛出错误,因此应该在try/catch块之外调用它。
redirect可以在渲染过程中在客户端组件中调用,但不能在事件处理程序中调用。您可以改用useRouterhook。
permantentRedirect 函数
permanentRedirect函数允许您永久将用户重定向到另一个URL。您可以在服务器组件、路由处理程序和服务器操作中调用permanentRedirect。
permanentRedirect通常在更改实体规范URL的突变或事件之后使用,例如在用户更改用户名后更新用户的配置文件URL:
'use server'
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function updateUsername(username: string, formData: FormData) {
try {
// Call database
} catch (error) {
// Handle errors
}
revalidateTag('username') // Update all references to the username
permanentRedirect(`/profile/${username}`) // Navigate to the new user profile
}
permanentRedirect默认返回308(永久重定向)状态码。
useRouter() hook
如果您需要在客户端组件中的事件处理程序内部重定向,您可以使用useRouter挂钩中的push方法。例如:
'use client'
import { useRouter } from 'next/navigation'
export default function Page() {
const router = useRouter()
return (
<button type="button" onClick={() => router.push('/dashboard')}>
Dashboard
</button>
)
}
如果您不需要以编程方式导航用户,则应该使用[
<Link>] 组件。
config中的redircets
next.config.js文件中的redirects选项允许您将传入请求路径重定向到不同的目标路径。当您更改页面的URL结构或拥有提前已知的重定向列表时,这很有用。
module.exports = {
async redirects() {
return [
// Basic redirect
{
source: '/about',
destination: '/',
permanent: true,
},
// Wildcard path matching
{
source: '/blog/:slug',
destination: '/news/:slug',
permanent: true,
},
]
},
}
中间件的NextResponse.redirect
中间件允许您在请求完成之前运行代码。然后,根据传入的请求,使用NextResponse.redirect重定向到不同的URL。如果您想根据条件(例如身份验证、会话管理等)重定向用户或有大量重定向,这很有用。
例如,如果用户未经身份验证,要将用户重定向到/login页面:
import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'
export function middleware(request: NextRequest) {
const isAuthenticated = authenticate(request)
// If the user is authenticated, continue as normal
if (isAuthenticated) {
return NextResponse.next()
}
// Redirect to login page if not authenticated
return NextResponse.redirect(new URL('/login', request.url))
}
export const config = {
matcher: '/dashboard/:path*',
}
中间件在
redirects之后运行。
大规模管理redirects
redirects可能对平台有限制。例如,在Vercel上,有1,024个重定向的限制。要管理大量重定向(1000多个),请见:nextjs.org/docs/app/bu…
路由组 (Route Groups)
在app目录中,嵌套文件夹通常映射到URL路径。但是,您可以将文件夹标记为Route Group以防止该文件夹包含在路由的URL路径中。
用途
形式
使用小括号包裹文件夹名称来创建路由组:(folderName)
案例
在不影响url的情况下组织路由
在如图所示的文件结构中,marketing和shop并不会作为路由url的一部分,仍然通过/about进行访问。
此外,即使(marketing)和(shop)中的路由共享相同的URL层次结构,您也可以通过在其文件夹中添加layout.js文件来为每个组创建不同的布局。
创建多个root layout
要创建多个根布局,请删除顶级layout.js文件,并在每个路由组中添加一个layout.js文件。这对于将应用程序划分为具有完全不同UI或体验的部分很有用。<html>和<body>标签需要添加到每个根布局中。
在上面的示例中,(marketing)和(shop)都有自己的根布局。
注意
路由组的命名除了对组织没有特殊意义。它们不影响URL路径。
包含路由组的路由不应该解析为与其他路由相同的URL路径。例如,由于路由组不影响URL结构,(marketing)/about/page.js和(shop)/about/page.js都会解析为/about并导致错误。
如果您使用多个根布局而没有顶级layout.js文件,则应在其中一个路由组中定义您的homepage.js文件,例如:app/(marketing)/page.js。
跨多个根布局导航将导致整页加载(与客户端导航相反)。例如,从使用app/(shop)/layout.js的/cart导航到使用app/(marketing)/layout.js的/blog将导致整页加载。此仅适用于多个根布局。
动态路由(Dynamic Routes)
使用
可以通过将文件夹的名称包装在方括号中来创建动态路由片段:[folderName]。例如,[id]或[slug]。
动态片段作为paramsprop传递给layout、page、route和generateMetadata函数。
案例
例如,博客可以包含以下路由app/blog/[slug]/page.js其中[slug]是博客文章的动态段。
export default function Page({ params }: { params: { slug: string } }) {
return <div>My Post: {params.slug}</div>
}
| Route 路线 | Example URL 示例URL | params |
|---|---|---|
app/blog/[slug]/page.js | /blog/a | { slug: 'a' } |
app/blog/[slug]/page.js | /blog/b | { slug: 'b' } |
app/blog/[slug]/page.js | /blog/c | { slug: 'c' } |
生成静态参数
generateStaticParams函数可以与动态路由段结合使用,以在构建时静态生成路由,而不是在请求时按需生成。
什么是 generateStaticParams?
generateStaticParams 是 Next.js 提供的一个函数,用于在构建(build)阶段静态生成动态路由的参数。这样可以在编译时预先生成对应的静态页面,而不是在每次请求时动态生成,从而提高性能和加载速度。
适用场景
当你有一些动态路由(如博客文章、产品详情页等),并且这些页面可以在构建时确定数据来源时,使用 generateStaticParams 是一个很好的选择。例如,你有一个博客应用,每篇文章都有一个唯一的 slug,你希望在构建时预生成所有文章的静态页面。
代码示例解析
让我们看看你提供的代码示例,并逐步解析:
// 文件路径: app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await fetch('https://.../posts').then((res) => res.json())
return posts.map((post) => ({
slug: post.slug,
}))
}
-
文件结构:
app/blog/[slug]/page.tsx表示这是一个动态路由页面,其中[slug]是一个动态参数。 -
generateStaticParams函数: 这是一个异步函数,用于生成所有可能的参数组合。在这个例子中,它生成所有可能的slug参数。 -
数据获取:
fetch('https://.../posts'):从一个 API(https://.../posts)获取所有博客文章的数据。
res.json():将响应转换为 JSON 格式。 -
生成参数:
posts.map((post) => ({ slug: post.slug })):遍历所有文章,提取每篇文章的slug,并返回一个对象数组,每个对象包含一个slug。
generateStaticParams 的主要优点
-
智能数据检索与缓存(Memoization) : 如果你在
generateStaticParams中使用fetch请求相同的参数,Next.js 会自动缓存这些请求。这意味着即使在多个generateStaticParams、布局(Layouts)和页面(Pages)中使用相同的fetch请求,也只会在构建时发出一次请求。这大大减少了构建时间,提高了效率。 -
静态生成的性能优势: 通过在构建时预生成所有页面,用户在访问页面时无需等待服务器生成内容,直接从 CDN 或服务器获取静态内容,提升了加载速度和用户体验。
-
SEO 友好: 预渲染的静态页面对于搜索引擎爬虫更友好,有助于搜索引擎更好地索引你的内容。
[...] Catch-all路由段
动态段可以通过在括号[...folderName]中添加省略号来扩展到catt-all后续路由片段。
例如,app/shop/[...slug]/page.js将匹配/shop/clothes,还可以匹配/shop/clothes/tops、/shop/clothes/tops/t-shirts,等等。
| Route 路线 | Example URL 示例URL | params |
|---|---|---|
app/shop/[...slug]/page.js | /shop/a | { slug: ['a'] } |
app/shop/[...slug]/page.js | /shop/a/b | { slug: ['a', 'b'] } |
app/shop/[...slug]/page.js | /shop/a/b/c | { slug: ['a', 'b', 'c'] } |
[[...]] 可选catch-all
通过在双方括号中包含参数,可以使所有段成为可选:[[...folderName]]。
例如,app/shop/[[...slug]]/page.js将也匹配/shop,除了/shop/clothes、/shop/clothes/tops、/shop/clothes/tops/t-shirts。
catch-all和可选catch-all段之间的区别在于,对于可选,没有参数的路由也匹配(上面示例中的/shop)。
| Route 路线 | Example URL 示例URL | params |
|---|---|---|
app/shop/[[...slug]]/page.js | /shop | {} |
app/shop/[[...slug]]/page.js | /shop/a | { slug: ['a'] } |
app/shop/[[...slug]]/page.js | /shop/a/b | { slug: ['a', 'b'] } |
app/shop/[[...slug]]/page.js | /shop/a/b/c | { slug: ['a', 'b', 'c'] } |
平行路由(Parallel Routes)
Parallel Routes 是 Next.js 13 引入的一项特性,它允许你在一个页面中并行加载多个路由,从而提高页面加载性能和用户体验。传统的路由通常是一个线性的结构,而 Parallel Routes 允许你在页面内的不同部分同时加载不同的内容。
主要概念
- 并行加载:多个路由可以在同一页面中同时加载,而不是按顺序逐步加载。这意味着当一个路由加载时,其他路由也可以并行加载,从而提高页面的整体加载速度。
- 嵌套路由:可以在一个页面中嵌套多个路由,这些路由可以独立管理自己的状态和数据。
- 布局(Layout)和嵌套布局:Next.js 13 引入了布局组件,可以用来定义页面的整体结构和样式。通过组合嵌套路由和布局组件,可以创建复杂的页面结构。