Skip to content

Next.js App 目录获取当前路径名

问题描述

在 Next.js 的 app 目录架构中(Next.js 13+),路由系统进行了重大重构。许多开发者尝试获取当前路径名(pathname)以实现导航高亮等常见功能时,会遇到以下问题:

tsx
import { useRouter } from 'next/navigation';

const router = useRouter();
// 错误:Property 'pathname' does not exist on type 'AppRouterInstance'
router.pathname;

传统方法不再适用于新的 app 路由架构:

  • next/router 被新的 next/navigation 模块取代
  • 原有的 useRouter 钩子不再提供 pathname 属性
  • 服务器组件和客户端组件需要不同的实现方法

这个变化导致导航栏链接高亮等常见功能无法直接实现,需要采用新的解决方案。

解决方案

客户端组件:使用 usePathname 钩子

app 目录架构中,Next.js 提供了专用的 usePathname 钩子来获取当前路径名。这是官方推荐的方式,适用于所有客户端组件。

jsx
'use client'; // 必须标记为客户端组件

import { usePathname } from 'next/navigation';
import Link from 'next/link';

export function NavLinks() {
  const pathname = usePathname(); // 获取当前路径名

  return (
    <nav>
      <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
        首页
      </Link>
      <Link
        className={`link ${pathname === '/about' ? 'active' : ''}`}
        href="/about"
      >
        关于
      </Link>
    </nav>
  );
}

使用须知

  1. 务必在组件顶部添加 'use client' 指令
  2. usePathname 只能在客户端组件中使用
  3. Next.js 13+ 所有版本都支持此方法

服务端组件获取方式

服务端组件无法使用钩子,但可以通过以下方法获取路径信息:

方法1:通过 headers

jsx
import { headers } from 'next/headers';

export default function Component() {
  const headerList = headers();
  const fullUrl = headerList.get('referer') || '';
  const activePath = fullUrl.split('/').pop(); // 简化的路径提取

  return <div>当前路径: {activePath}</div>;
}

注意

实际项目中需要更可靠的路径解析:

js
const origin = headerList.get('origin') || '';
const referer = headerList.get('referer') || '';
const pathname = referer.replace(origin, '');

方法2:利用动态路由参数

对于 /products/[id] 这类动态路由,可从 params 获取参数:

jsx
// 文件路径: app/products/[id]/page.js
export default function Page({ params }) {
  // 访问 /products/123 → id = "123"
  const productId = params.id; 

  return <div>产品ID: {productId}</div>;
}

兼容性说明

Next.js 15+ 改变了动态路由的行为:

js
// Next.js 15+ 需要异步方式
export default async function Page({ params }) {
  const { id } = await params; // 新增 await
  return <div>产品ID: {id}</div>;
}

替代解决方案

1. 中间件传递方案

可以通过中间件向服务端组件传递路径信息:

js
// middleware.js
export function middleware(request) {
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-pathname', request.nextUrl.pathname);

  return NextResponse.next({ request: { headers: requestHeaders } });
}
js
// 组件中
import { headers } from 'next/headers';

export default function Component() {
  const headersList = headers();
  const pathname = headersList.get('x-pathname');
}

2. 使用第三方库(不推荐)

bash
npm install next-extra
js
import { headers } from 'next/headers';
import { pathname } from 'next-extra/pathname';

export default function Layout({ children }) {
  const base = `https://${headers().get('host')}`;
  const currentUrl = new URL(pathname(), base);
}

官方建议

优先使用官方特性如 usePathnameheaders 而非第三方库,确保长期兼容性

最佳实践示例:导航栏高亮显示

客户端实现方案

jsx
'use client';

import { usePathname } from 'next/navigation';
import Link from 'next/link';

const NavLink = ({ href, children }) => {
  const pathname = usePathname();
  const isActive = pathname === href;

  return (
    <Link href={href} className={isActive ? 'text-blue-600 font-bold' : 'text-gray-600'}>
      {children}
    </Link>
  );
};

// 在布局组件中使用
export default function Navbar() {
  return (
    <nav className="flex space-x-4">
      <NavLink href="/">首页</NavLink>
      <NavLink href="/about">关于</NavLink>
      <NavLink href="/contact">联系我们</NavLink>
    </nav>
  );
}

服务器端替代方案

jsx
import { headers } from 'next/headers';
import Link from 'next/link';

const getCurrentPath = () => {
  const headersList = headers();
  const origin = headersList.get('x-forwarded-host') || '';
  const referer = headersList.get('referer') || '';
  return referer.replace(`https://${origin}`, '');
};

export default function Navbar() {
  const currentPath = getCurrentPath();

  return (
    <nav className="flex space-x-4">
      <Link
        href="/"
        className={currentPath === '/' ? 'active' : ''}
      >
        首页
      </Link>
      {/* 其他链接 */}
    </nav>
  );
}

实际应用建议

  1. 导航组件通常使用客户端方案实现:实时响应路由变化
  2. 服务器端方案适用于静态路径数据的展示:SEO优化

常见问题解决

为什么某些场景无法获取完整URL?
  • headers() 在静态渲染页面中无法使用
  • 通过 next.config.js 配置动态渲染:
js
// next.config.js
exports.config = {
  dynamic: 'force-dynamic', // 强制开启动态渲染
};

版本兼容性

  • Next.js 13.4+:推荐使用 App Router 和 usePathname
  • Next.js 15:动态路由变为异步(需 await params
  • 升级说明请参考官方升级指南