一、 路由与导航 (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>
);
}优点: usePathname 比 useRouter 更轻量。当只有路径变化时,仅使用此 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">)主要通过 metadata 或 generateMetadata 导出对象来管理。但对于需要在客户端动态修改元数据的场景,可以使用 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 Router | Router 对象 | App Router 版更精简,专注于导航。 |
usePathname | (只读) 获取当前 URL 路径名 | App Router | string | 性能更优,只关心路径。 |
useSearchParams | (只读) 获取 URL 查询参数 | App Router | URLSearchParams | 性能更优,只关心查询参数。建议与 Suspense 配合。 |
useParams | (只读) 获取动态路由参数 | App Router | object | 客户端获取动态路由值的标准方式。 |
useSelectedLayoutSegment(s) | (只读) 在布局中确定活动的子路由 | App Router | string / string[] | 用于构建复杂的、感知路由的布局。 |
核心要点:
- 客户端专属: 上述所有 Next.js Hooks 都必须在标记为
'use client'的客户端组件中使用。它们依赖于浏览器环境和 React 的状态管理。 - 细粒度原则 (App Router): App Router 鼓励使用更细粒度的 Hooks (
usePathname,useSearchParams,useParams),而不是一把抓的useRouter。这使得组件能够更精确地响应它们所依赖的路由部分的变化,从而优化渲染性能。 - 数据获取: Next.js 没有一个官方的
useData或useFetchHook。数据获取的模式是:- 首选: 在服务端组件中通过
async/awaitfetch获取数据,通过 props 传递。 - 客户端: 当需要在客户端(如用户交互后)获取数据时,官方推荐使用 SWR (
useSWR) 或其他数据获取库(如 TanStack Query)。useSWR由 Vercel 团队开发,与 Next.js 配合得天衣无缝。
- 首选: 在服务端组件中通过
希望这个详细的解释能帮助你更好地理解和使用 Next.js 中的 Hooks!