服务器端使用

服务器端类型安全的搜索参数

Loaders

Introduced in version 2.3.0.

要在服务器端解析搜索参数,您可以使用 loader 函数。

您可以使用 createLoader 函数创建一个 loader,通过传递搜索参数描述符对象来实现:

searchParams.tsx
import { parseAsFloat, createLoader } from 'nuqs/server'

// 描述您的搜索参数,并在 useQueryStates / createSerializer 中重用它:
export const coordinatesSearchParams = {
  latitude: parseAsFloat.withDefault(0),
  longitude: parseAsFloat.withDefault(0)
}

export const loadSearchParams = createLoader(coordinatesSearchParams)

在这里,loadSearchParams 是一个解析搜索参数并返回服务器端可消费的状态变量的函数(与 useQueryStates 返回的相同状态类型)。

app/page.tsx
import { loadSearchParams } from './search-params'
import type { SearchParams } from 'nuqs/server'

type PageProps = {
  searchParams: Promise<SearchParams>
}

export default async function Page({ searchParams }: PageProps) {
  const { latitude, longitude } = await loadSearchParams(searchParams)
  return <Map
    lat={latitude}
    lng={longitude}
  />

  // 专业提示:您不必等待结果。
  // 将 Promise 对象传递给用 <Suspense> 包裹的子组件
  // 以受益于 PPR / dynamicIO,并在搜索参数可用时立即提供静态外壳,
  // 同时流式传输依赖于搜索参数的动态部分。
}
pages/index.tsx
import type { GetServerSidePropsContext } from 'next'

export async function getServerSideProps({ query }: GetServerSidePropsContext) {
  const { latitude, longitude } = loadSearchParams(query)
  // 使用坐标进行一些服务器端计算
  return {
    props: { ... }
  }
}
app/routes/_index.tsx
export function loader({ request }: LoaderFunctionArgs) {
  const { latitude, longitude } = loadSearchParams(request) // request.url 也可以
  // 使用坐标进行一些服务器端计算
  return ...
}
// 注意:您也可以在客户端(或任何地方)使用它,
// 用于一次性解析非响应式搜索参数:

loadSearchParams('https://example.com?latitude=42&longitude=12')
loadSearchParams(location.search)
loadSearchParams(new URL(...))
loadSearchParams(new URLSearchParams(...))
// App router,例如:app/api/location/route.ts
export async function GET(request: Request) {
  const { latitude, longitude } = loadSearchParams(request)
  // ...
}

// Pages router,例如:pages/api/location.ts
import type { NextApiRequest, NextApiResponse } from 'next'
export default function handler(
  request: NextApiRequest,
  response: NextApiResponse
) {
  const { latitude, longitude } = loadSearchParams(request.query)
}

注意

Loader 不会验证您的数据。如果您期望正整数 或特定形状的 JSON 编码对象,您需要将 loader 的结果 传递给模式验证库,例如 Zod

内置验证支持即将到来。阅读 RFC。 或者,您可以构建验证到 自定义解析器 中。

Loader 函数将接受以下输入类型来解析搜索参数:

  • 包含完整 URL 的字符串:https://example.com/?foo=bar
  • 仅包含搜索参数的字符串:?foo=bar(类似于 location.search
  • 一个 URL 对象
  • 一个 URLSearchParams 对象
  • 一个 Request 对象
  • 一个 Record<string, string | string[] | undefined>(例如:{ foo: 'bar' }
  • 以上任何类型的 Promise,在这种情况下,它也会返回一个 Promise。

严格模式

Introduced in version 2.5.0.

如果搜索参数包含与关联解析器不兼容的值(例如:?count=banana 用于 parseAsInteger), 默认行为是如果指定了默认值,则返回 默认值,否则返回 null

您可以开启 严格模式,以便在运行 loader 时对无效值抛出错误:

const loadSearchParams = createLoader({
  count: parseAsInteger.withDefault(0)
})

// 默认:将返回 { count: 0 }
loadSearchParams('?count=banana')

// 严格模式:将抛出错误
loadSearchParams('?count=banana', { strict: true })
// [nuqs] 解析查询 `banana` 的键 `count` 时出错

Cache

Introduced in version 1.13.0.

如果您希望在深度嵌套的服务器组件中访问 searchParams (即不在页面组件中),您可以使用 createSearchParamsCache 以类型安全的方式实现。

可以将它视为一个 loader 与一种传播解析值到 RSC 树的方式的组合,就像客户端上的 Context 一样。

searchParams.ts
import {
  createSearchParamsCache,
  parseAsInteger,
  parseAsString
} from 'nuqs/server'
// 注意:从 'nuqs/server' 导入以避免 "use client" 指令

export const searchParamsCache = createSearchParamsCache({
  // 在此处列出您的搜索参数键和关联解析器:
  q: parseAsString.withDefault(''),
  maxResults: parseAsInteger.withDefault(10)
})
page.tsx
import { searchParamsCache } from './searchParams'
import { type SearchParams } from 'nuqs/server'

type PageProps = {
  searchParams: Promise<SearchParams> // Next.js 15+:异步 searchParams 属性
}

export default async function Page({ searchParams }: PageProps) {
  // ⚠️ 不要忘记在此处调用 `parse`。
  // 您可以从返回的对象中访问类型安全的数值:
  const { q: query } = await searchParamsCache.parse(searchParams)
  return (
    <div>
      <h1>搜索结果:{query}</h1>
      <Results />
    </div>
  )
}

function Results() {
  // 在子服务器组件中访问类型安全的搜索参数:
  const maxResults = searchParamsCache.get('maxResults')
  return <span>最多显示 {maxResults} 个结果</span>
}

Cache 仅在当前页面渲染期间有效 (参见 React 的 cache 函数)。

注意:cache 仅适用于 服务器组件,但您可以将 您的解析器声明与 useQueryStates 共享,以在客户端组件中实现类型安全:

searchParams.ts
import {
  parseAsFloat,
  createSearchParamsCache
} from 'nuqs/server'

export const coordinatesParsers = {
  lat: parseAsFloat.withDefault(45.18),
  lng: parseAsFloat.withDefault(5.72)
}
export const coordinatesCache = createSearchParamsCache(coordinatesParsers)
page.tsx
import { coordinatesCache } from './searchParams'
import { Server } from './server'
import { Client } from './client'

export default async function Page({ searchParams }) {
  // 注意:您也可以在此处使用严格模式:
  await coordinatesCache.parse(searchParams, { strict: true })
  return (
    <>
      <Server />
      <Suspense>
        <Client />
      </Suspense>
    </>
  )
}
server.tsx
import { coordinatesCache } from './searchParams'

export function Server() {
  const { lat, lng } = coordinatesCache.all()
  // 或者单独访问键:
  const lat = coordinatesCache.get('lat')
  const lng = coordinatesCache.get('lng')
  return (
    <span>
      纬度:{lat} - 经度:{lng}
    </span>
  )
}
client.tsx
'use client'

import { useQueryStates } from 'nuqs'
import { coordinatesParsers } from './searchParams'

export function Client() {
  const [{ lat, lng }, setCoordinates] = useQueryStates(coordinatesParsers)
  // ...
}

更短的搜索参数键

就像使用 useQueryStates 一样,您可以 定义一个 urlKeys 对象来将解析器定义的变量名映射到 URL 中的更短键。它们将被读取时翻译,并且您的代码库 只能引用对您的领域或业务逻辑有意义的变量名。

searchParams.ts
export const coordinatesParsers = {
  // 在整个代码库中使用人类可读的变量名
  latitude: parseAsFloat.withDefault(45.18),
  longitude: parseAsFloat.withDefault(5.72)
}
export const coordinatesCache = createSearchParamsCache(coordinatesParsers, {
  urlKeys: {
    // 将它们重新映射为从 URL 中的更短键读取
    latitude: 'lat',
    longitude: 'lng'
  }
})