Skip to content

获取查询参数(Next.js 服务器组件)

问题

在 Next.js 13 中引入了全新的 app 目录架构,组件默认成为"服务器组件"(Server Components)。这些组件具有特殊能力:

  1. 支持 async/await 执行异步操作
  2. 可直接访问 cookies() 获取 Cookie
  3. 可直接访问 headers() 获取请求头

但官方文档未说明如何直接在服务器组件中获取 URL 查询参数(query params)。在 Next.js 13 之前的 getServerSideProps 方法中,可通过 context.query 轻松访问查询参数,但在新的服务器组件架构中,这一常用功能如何实现?

解决方案

方法一:在页面组件中直接获取 (推荐)

app 目录下的页面文件(page.tsx/page.jsx)中,Next.js 会自动提供 searchParams 属性:

tsx
export default function Page({
  searchParams,
}: {
  searchParams?: { [key: string]: string | string[] | undefined };
}) {
  // 获取查询参数
  const filter = searchParams?.filter;
  const page = searchParams?.page;

  return (
    <div>
      <h1>筛选器:{filter}</h1>
      <p>页码:{page || 1}</p>
    </div>
  );
}
  • 访问方式searchParams?.参数名(可选链避免未定义错误)
  • 参数值类型string | string[] | undefined
  • 适用场景:页面级组件获取 URL 参数
  • 优势:官方推荐方式,简洁直接

方法二:在 API 路由中获取

app/api 目录下的路由处理器(route.ts/route.js)中处理请求时:

ts
export async function GET(request: Request) {
  // 解析URL参数
  const { searchParams } = new URL(request.url);
  
  // 获取具体参数值
  const skip = searchParams.get("skip")
  const limit = searchParams.get("limit")

  // 使用参数处理业务逻辑
  return new Response(JSON.stringify({ skip, limit }), {
    status: 200,
    headers: { "Content-Type": "application/json" }
  });
}
  • 访问方式new URL(request.url).searchParams
  • 参数方法
    • get():获取单个参数
    • getAll():获取数组类型参数
    • entries():遍历所有参数
  • 适用场景:API 路由处理程序

方法三:在组件中获取路径参数

访问动态路由中的路径参数(如 app/category/[filter]/page.tsx):

tsx
const Page = async ({
  params,
  searchParams
}: {
  params: { filter: string };
  searchParams: { [key: string]: string | string[] | undefined };
}) => {
  // 路径参数 - 来自 segment
  const filter = params.filter;
  
  // 查询参数 - 来自 URL
  const page = searchParams.page;
  
  return (
    <div>
      <h2>路径参数:{filter}</h2>
      <p>查询参数:{page}</p>
    </div>
  )
}

方法四:通过中间件全局传递(任意组件)

TIP

当需要在非页面组件中访问查询参数时,可通过中间件全局传递

  1. 创建中间件(middleware.ts):
ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 将完整URL存入请求头
  const headers = new Headers(request.headers);
  headers.set('x-url', request.url);
  
  return NextResponse.next({ request: { headers } });
}
  1. 在任意服务器组件中使用:
tsx
import { headers } from 'next/headers';

export default function ProductList() {
  // 从header获取存储的URL
  const headerList = headers();
  const fullUrl = headerList.get('x-url') || '';
  
  // 解析查询参数
  const url = new URL(fullUrl);
  const minPrice = url.searchParams.get('min_price');
  
  return (
    <div>
      筛选的价格下限:{minPrice}
    </div>
  );
}

常见错误解决方案

错误:searchParams is undefined

tsx
// 错误情况:未正确声明类型
export default function Page({ searchParams }) { /* ... */ }

解决方案: 明确声明类型

tsx
export default function Page({
  searchParams
}: {
  searchParams?: { [key: string]: string | string[] | undefined };
}) {
  // ...
}

最佳实践建议

  1. 优先选择在页面组件获取

    • 适用于大多数场景
    • 避免过度设计
  2. 安全注意

    tsx
    <div dangerouslySetInnerHTML={{ __html: searchParams.unsafe }} />
    • 禁止直接渲染未处理的查询参数内容
    • 应对参数值进行安全过滤
  3. 参数格式处理

    tsx
    const page = parseInt(searchParams?.page as string || '1')
    • 数字参数需显式类型转换
    • 设置合理的参数默认值

总结

场景推荐方法示例代码
页面组件获取查询参数searchParams 属性export default Page({ searchParams })
API路由获取查询参数URLURLSearchParamsnew URL(request.url).searchParams
获取动态路由路径参数params 属性export default Page({ params })
任意组件参数访问中间件+请求头传递见"方法四"详细实现

:::success 最佳实践建议:优先使用页面组件自带的 searchParams 属性获取查询参数,只有在特殊情况下才采用中间件等复杂方案 :::