Skip to Content

一、 路由与导航 (Routing & Navigation)

这是最常用的一类 Hooks,它们让你能够与 Next.js 强大的路由系统进行交互。

1. useRouter (适用于 Pages Router 和 App Router)

useRouter 是最经典、功能最全面的路由 Hook。它返回一个路由器(router)对象,包含了当前路由的各种信息和进行导航的方法。

何时使用:

  • 需要进行编程式导航(即通过代码跳转页面,而不是通过 <Link> 组件)。
  • 需要同时访问路径、查询参数以及导航方法。

返回值 (部分核心属性):

  • router.push(path): 客户端导航到新页面,并将新页面添加到浏览器历史记录中。
  • router.replace(path): 客户端导航到新页面,但替换当前的历史记录项。
  • router.back(): 返回到历史记录中的上一页。
  • router.prefetch(path): 在后台预取页面资源,加快后续导航速度。
  • router.query: 一个对象,包含了动态路由参数和查询字符串参数(在 Pages Router 中非常常用)。
  • router.pathname: 当前页面的路径名(不含查询参数)。
  • router.isReady: 一个布尔值,在 Pages Router 中,表示路由器字段(如 query)是否已在客户端更新并准备就绪。

示例 (App Router):

// app/components/LoginButton.js 'use client'; // 必须是客户端组件 import { useRouter } from 'next/navigation'; // 注意导入来源是 next/navigation export default function LoginButton() { const router = useRouter(); const handleLogin = () => { // 假设登录成功 console.log('Login successful, redirecting to dashboard...'); router.push('/dashboard'); }; return <button onClick={handleLogin}>Login and Go to Dashboard</button>; }

注意: 在 App Router 中,useRouter 的导入来源是 next/navigation,而在 Pages Router 中是 next/router。App Router 中的 useRouter 返回值更精简,不包含 pathname, query 等属性,因为这些被新的、更细粒度的 Hooks 替代了。


2. usePathname (仅限 App Router)

这是一个只读的 Hook,用于获取当前页面的路径名 (pathname)。

何时使用:

  • 当你只需要知道当前的 URL 路径,例如用于高亮导航栏中的活动链接。

返回值:

  • 一个字符串,如 /dashboard/settings

示例:

// app/components/ActiveLink.js 'use client'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; export default function ActiveLink({ href, children }) { const pathname = usePathname(); const isActive = pathname === href; return ( <Link href={href} style={{ color: isActive ? 'blue' : 'black' }}> {children} </Link> ); }

优点: usePathnameuseRouter 更轻量。当只有路径变化时,仅使用此 Hook 的组件会重新渲染,而不会因为查询参数的变化而重新渲染,从而实现更优的性能。


3. useSearchParams (仅限 App Router)

这是一个只读的 Hook,用于获取当前 URL 的查询字符串参数 (search params)。

何时使用:

  • 需要根据 URL 中的查询参数(如 ?q=search&page=2)来渲染不同内容或执行逻辑。

返回值:

  • 一个只读的 URLSearchParams 对象实例。你需要使用 .get(), .getAll(), .has() 等方法来读取参数。

示例:

// app/search/page.js 'use client'; import { useSearchParams } from 'next/navigation'; import { Suspense } from 'react'; function SearchResults() { const searchParams = useSearchParams(); const query = searchParams.get('q'); const sortBy = searchParams.get('sortBy') || 'relevance'; // 基于 query 和 sortBy 来获取和显示数据... return ( <div> <h1>Showing results for: "{query}"</h1> <p>Sorted by: {sortBy}</p> </div> ); } // 建议将使用 useSearchParams 的组件包裹在 Suspense 中 // 因为它会在页面首次渲染时等待参数可用,可能会触发 Suspense Fallback export default function SearchPage() { return ( <Suspense fallback={<div>Loading...</div>}> <SearchResults /> </Suspense> ) }

4. useParams (仅限 App Router)

这是一个只读的 Hook,用于获取当前路由的动态路由参数。

何时使用:

  • 在动态路由页面(如 app/blog/[slug]/page.js)中,获取动态段的值(如 slug)。

返回值:

  • 一个对象,键是动态路由段的名称,值是其具体内容。

示例 (app/products/[id]/page.js):

// app/products/[id]/components/ProductDetails.js 'use client'; import { useParams } from 'next/navigation'; import { useEffect, useState } from 'react'; export default function ProductDetails() { const params = useParams(); // params 会是 { id: '...' } const [product, setProduct] = useState(null); useEffect(() => { // 在客户端根据 id 获取商品数据 fetch(`/api/products/${params.id}`) .then((res) => res.json()) .then(setProduct); }, [params.id]); if (!product) return <div>Loading product...</div>; return <h1>{product.name}</h1>; }

最佳实践提醒: 虽然上面的例子可以在客户端获取数据,但在 App Router 中,更推荐的做法是在服务端组件 (page.js) 中通过 params props 接收动态参数并获取数据,然后将数据作为 props 传递给客户端组件。这样可以利用服务器端渲染 (SSR) 或静态生成 (SSG) 的优势。

// app/products/[id]/page.js (服务端组件) async function getProduct(id) { const res = await fetch(`https://api.example.com/products/${id}`); return res.json(); } export default async function ProductPage({ params }) { // params 直接从 props 中获取,无需 hook const product = await getProduct(params.id); // 可以将数据传递给客户端组件 return <ProductDetails initialProduct={product} />; }

二、元数据 (Metadata)

在 App Router 中,元数据(如页面标题 <title>、描述 <meta name="description">)主要通过 metadatagenerateMetadata 导出对象来管理。但对于需要在客户端动态修改元数据的场景,可以使用 use-metadata 这样的社区库或手动操作 DOM,Next.js 本身没有提供一个官方的 useMetadata Hook。

三、其他特殊用途的 Hooks

useSelectedLayoutSegment / useSelectedLayoutSegments (仅限 App Router)

这些是更高级的 Hooks,通常用于复杂的布局,如并排路由 (Parallel Routes) 或拦截路由 (Intercepting Routes)。

  • useSelectedLayoutSegment(parallelRoutesKey): 读取由特定 layout 下的 page.js 渲染的路由段。
  • useSelectedLayoutSegments(): 返回一个数组,包含从根布局到当前页面的所有活动路由段。

何时使用:

  • 当你在一个共享的布局(如 layout.js)中,需要知道哪个子页面或子路由当前是活动的,以便构建动态的 UI(例如,一个始终显示活动选项卡的选项卡栏)。

示例 (app/dashboard/layout.js)

'use client'; import { useSelectedLayoutSegment } from 'next/navigation'; import Link from 'next/link'; export default function DashboardLayout({ children }) { // `segment` 的值会是 'settings', 'profile', 或 null (当在 /dashboard 时) const segment = useSelectedLayoutSegment(); return ( <div> <nav> <Link href="/dashboard/settings" style={{ textDecoration: segment === 'settings' ? 'underline' : 'none' }}> Settings </Link> <Link href="/dashboard/profile" style={{ textDecoration: segment === 'profile' ? 'underline' : 'none' }}> Profile </Link> </nav> {children} </div> ); }

总结与对比

Hook主要用途适用范围返回值类型备注
useRouter编程式导航、访问完整路由信息Pages & App RouterRouter 对象App Router 版更精简,专注于导航。
usePathname(只读) 获取当前 URL 路径名App Routerstring性能更优,只关心路径。
useSearchParams(只读) 获取 URL 查询参数App RouterURLSearchParams性能更优,只关心查询参数。建议与 Suspense 配合。
useParams(只读) 获取动态路由参数App Routerobject客户端获取动态路由值的标准方式。
useSelectedLayoutSegment(s)(只读) 在布局中确定活动的子路由App Routerstring / string[]用于构建复杂的、感知路由的布局。

核心要点:

  1. 客户端专属: 上述所有 Next.js Hooks 都必须在标记为 'use client' 的客户端组件中使用。它们依赖于浏览器环境和 React 的状态管理。
  2. 细粒度原则 (App Router): App Router 鼓励使用更细粒度的 Hooks (usePathname, useSearchParams, useParams),而不是一把抓的 useRouter。这使得组件能够更精确地响应它们所依赖的路由部分的变化,从而优化渲染性能。
  3. 数据获取: Next.js 没有一个官方的 useDatauseFetch Hook。数据获取的模式是:
    • 首选: 在服务端组件中通过 async/await fetch 获取数据,通过 props 传递。
    • 客户端: 当需要在客户端(如用户交互后)获取数据时,官方推荐使用 SWR (useSWR) 或其他数据获取库(如 TanStack Query)。useSWR 由 Vercel 团队开发,与 Next.js 配合得天衣无缝。

希望这个详细的解释能帮助你更好地理解和使用 Next.js 中的 Hooks!

Last updated on