从0死磕全栈之Next.js 重定向全指南:从基础跳转到大规模路由迁移

"好的重定向,是用户体验与 SEO 的隐形守护者。"

在现代 Web 应用中,重定向(Redirect)不仅是页面跳转的技术手段,更是保障 SEO 连续性、维护用户会话、实现权限控制 的关键机制。Next.js 提供了五种不同层级的重定向能力,覆盖从静态配置到动态逻辑的全场景需求。

本文将系统梳理 Next.js 中的重定向方案,帮助你精准选择合适的方式,避免常见陷阱。


一、五种重定向方式概览

方法 适用场景 执行时机 状态码 位置
redirect() 表单提交后跳转、服务端事件响应 渲染过程中 307(临时) / 303(Server Action) Server Component / Server Action / Route Handler
permanentRedirect() URL 永久变更(如用户名修改) 渲染过程中 308(永久) Server Component / Server Action / Route Handler
useRouter().push() 客户端交互跳转(按钮点击等) 浏览器事件中 ---(客户端导航) Client Component 事件处理器
redirects in next.config.js 静态路由迁移、已知路径重写 请求进入时 307 / 308 构建时配置
NextResponse.redirect() 动态条件跳转(如登录拦截) Middleware 中 任意 Middleware

核心原则

  • 服务端跳转 → 用 redirect / permanentRedirect / next.config.js / Middleware
  • 客户端跳转 → 用 useRouter

二、服务端重定向:redirectpermanentRedirect

2.1 临时重定向:redirect()

适用于表单提交后跳转、创建资源后导航等场景:

ts 复制代码
// app/actions.ts
'use server'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'

export async function createPost(formData: FormData) {
  const title = formData.get('title') as string
  
  // 1. 创建文章(伪代码)
  const id = await db.post.create({ title })

  // 2. 更新缓存
  revalidatePath('/posts')

  // 3. 跳转到新文章页
  redirect(`/posts/${id}`) // ← 307 临时重定向
}

⚠️ 注意

  • redirect() 会抛出异常,不要放在 try/catch 内部
  • 在 Server Action 中返回 303 See Other(符合 POST 后跳转规范)

2.2 永久重定向:permanentRedirect()

适用于URL 永久变更,如用户修改用户名后旧链接失效:

ts 复制代码
// app/actions.ts
'use server'
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'

export async function updateUsername(newUsername: string) {
  await db.user.update({ username: newUsername })
  revalidateTag('profile')
  permanentRedirect(`/profile/${newUsername}`) // ← 308 永久重定向
}

SEO 友好:搜索引擎会将旧 URL 的权重转移到新 URL。


三、客户端跳转:useRouter

用于用户交互触发的导航,如按钮点击、表单提交(非 Server Action):

tsx 复制代码
// app/components/GoToDashboard.tsx
'use client'
import { useRouter } from 'next/navigation'

export default function GoToDashboard() {
  const router = useRouter()

  return (
    <button onClick={() => router.push('/dashboard')}>
      进入控制台
    </button>
  )
}

最佳实践

  • 非交互式跳转优先用 <Link>(支持预加载)
  • 仅在事件处理器中使用 useRouter

四、静态重定向:next.config.js 中的 redirects

适用于已知的、静态的路径映射,如网站改版、旧博客迁移:

js 复制代码
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async redirects() {
    return [
      // 基础重定向
      {
        source: '/about',
        destination: '/',
        permanent: true, // 308
      },
      // 通配符匹配
      {
        source: '/blog/:slug',
        destination: '/news/:slug',
        permanent: true,
      },
      // 带查询参数
      {
        source: '/old-search',
        destination: '/search?q=legacy',
        permanent: false, // 307
      },
    ]
  },
}

export default nextConfig

⚠️ 限制

  • Vercel 平台最多支持 1024 条 redirects
  • 超过此数量需改用 Middleware + 数据库

五、动态重定向:Middleware 中的 NextResponse.redirect

适用于基于用户状态的条件跳转,如未登录用户访问受保护页面:

ts 复制代码
// middleware.ts
import { NextResponse, NextRequest } from 'next/server'
import { getAuth } from '@/lib/auth'

export function middleware(request: NextRequest) {
  const isAuthenticated = getAuth(request)

  if (!isAuthenticated && request.nextUrl.pathname.startsWith('/dashboard')) {
    // 重定向到登录页,保留原路径
    const url = new URL('/login', request.url)
    url.searchParams.set('from', request.nextUrl.pathname)
    return NextResponse.redirect(url)
  }

  return NextResponse.next()
}

export const config = {
  matcher: ['/dashboard/:path*', '/settings/:path*'],
}

优势

  • 支持任意状态码
  • 可访问 cookies、headers、session
  • 执行早于页面渲染

六、大规模重定向:1000+ 路由迁移方案

当重定向规则超过 1000 条(如 CMS 迁移),推荐 Middleware + Bloom Filter 方案:

步骤 1:构建重定向映射(JSON 或数据库)

json 复制代码
// redirects.json
{
  "/old-post-1": { "destination": "/news/post-1", "permanent": true },
  "/legacy/page": { "destination": "/new/page", "permanent": false }
}

步骤 2:Middleware 中使用 Bloom Filter 快速过滤

ts 复制代码
// middleware.ts
import { ScalableBloomFilter } from 'bloom-filters'
import bloomData from './bloom-filter.json'

const bloom = ScalableBloomFilter.fromJSON(bloomData)

export async function middleware(request: NextRequest) {
  const path = request.nextUrl.pathname

  if (bloom.has(path)) {
    // 可能存在重定向,交由 API 验证
    const res = await fetch(`/api/redirect?path=${encodeURIComponent(path)}`)
    if (res.ok) {
      const { destination, permanent } = await res.json()
      return NextResponse.redirect(destination, permanent ? 308 : 307)
    }
  }

  return NextResponse.next()
}

步骤 3:Route Handler 返回真实重定向

ts 复制代码
// app/api/redirect/route.ts
import redirects from '@/redirects.json'

export async function GET(request: NextRequest) {
  const path = request.nextUrl.searchParams.get('path')
  if (!path || !redirects[path]) {
    return new Response(null, { status: 404 })
  }
  return Response.json(redirects[path])
}

性能优势

  • Bloom Filter 内存占用小,误判率可控
  • 避免将全部重定向规则加载到 Middleware

七、常见陷阱与最佳实践

❌ 错误:在 try/catch 内调用 redirect

ts 复制代码
// 危险!redirect 会被 catch 捕获,导致跳转失效
try {
  await createPost()
  redirect('/success') // ❌ 不会执行
} catch (e) {
  // ...
}

正确做法 :将 redirect 放在 try 外部

ts 复制代码
await createPost()
redirect('/success') // ✅

❌ 错误:在 Client Component 渲染时调用 redirect

tsx 复制代码
// app/page.tsx
export default function Page() {
  redirect('/login') // ❌ 仅限 Server Component
}

正确做法 :服务端逻辑放 Server Component,客户端跳转用 useRouter

✅ 最佳实践总结

  • SEO 重定向 → 用 next.config.jspermanentRedirect
  • 权限控制 → 用 Middleware
  • 表单提交后跳转 → 用 Server Action + redirect
  • 用户交互跳转 → 用 useRouter
  • 大规模迁移 → 用 Middleware + Bloom Filter

结语

Next.js 的重定向体系体现了其"渐进式复杂度"的设计哲学:

  • 简单场景 → 一行配置搞定
  • 复杂场景 → 灵活组合 Middleware、Server Action 与缓存机制

掌握这五种重定向方式,你不仅能优雅处理页面跳转,更能构建健壮、可维护、SEO 友好的现代 Web 应用。

记住:重定向不是"绕路",而是为用户和搜索引擎铺设的"正确路径"。

相关推荐
沙蒿同学8 分钟前
我把架构约定编译成了会变红的测试:Wails v2 + Go + Vue3 桌面脚手架实战
前端·后端·github
cpolar技术支持11 分钟前
本地 Playwright 测试报告怎么远程复盘?Trace Viewer 跑起来后,用 cpolar 分享失败现场
前端·自动化测试·测试工具·cpolar·playwright
wordbaby11 分钟前
企业级后台管理系统路由设计与最佳实践指南
前端
胡志辉的博客21 分钟前
【完全开源】IP 纯净度检测 可一键部署到自己的CF
前端·javascript·chrome·ip·chromium
Hilaku1 小时前
作为面试官,我最怕遇到什么样的候选人?
前端·javascript·程序员
TiDi1 小时前
Pinia优化重复请求
前端
web3d5201 小时前
01-用 Leafletjs 10 分钟搭一张水利一张图(Vue3 + Vite 实战)
前端·javascript
晚安日记wanna2 小时前
Vue2 的 defineProperty 差在哪四层追问筛掉九成候选人
前端·vue.js·面试
TiDi2 小时前
吸顶导航交互实现
前端
kisshyshy2 小时前
从Props透传到自定义Hook:系统梳理React跨层级通信与逻辑复用
前端·架构·代码规范