Parsers

内置解析器

当字符串不够用时

搜索参数默认是字符串,但你的状态很可能比这更复杂。

你可能想要使用数字、布尔值、Date、对象、数组,甚至自定义类型。 这就是解析器派上用场的地方。

nuqs 为最常见类型提供了内置解析器,并允许你定义自己的

字符串

import { parseAsString } from 'nuqs'
Demo loading...

类型安全提示

parseAsString 是一个空操作:它在解析时不执行任何验证, 并会接受任何值。

如果你期望某些特定的字符串值,例如 'foo' | 'bar', 请参阅字面量 以确保类型运行时安全。

如果搜索参数默认就是字符串,这个_“解析器”_ 有什么意义?

如果声明搜索参数对象,和/或想要使用构建器模式来指定默认值选项,它就会变得有用:

export const searchParamsParsers = {
  q: parseAsString.withDefault('').withOptions({
    shallow: false
  })
}

数字

整数

使用 parseInt(基数 10)将搜索参数字符串转换为整数。

import { parseAsInteger } from 'nuqs'

useQueryState('int', parseAsInteger.withDefault(0))
Demo loading...

浮点数

与整数相同,但底层使用 parseFloat

import { parseAsFloat } from 'nuqs'

useQueryState('float', parseAsFloat.withDefault(0))
Demo loading...

十六进制

以十六进制编码整数。

import { parseAsHex } from 'nuqs'

useQueryState('hex', parseAsHex.withDefault(0x00))
Demo loading...

进一步探索

查看 Hex Colors playground 以获取演示。

索引

与整数相同,但对序列化的查询字符串添加 +1 偏移(解析时为 -1)。 适用于分页索引。

import { parseAsIndex } from 'nuqs'

const [pageIndex] = useQueryState('page', parseAsIndex.withDefault(0))
Demo loading...

布尔值

import { parseAsBoolean } from 'nuqs'

useQueryState('bool', parseAsBoolean.withDefault(false))
Demo loading...

字面量

这些解析器扩展了基本的整数和浮点解析器,但会针对一些预期值进行验证,这些值定义为 TypeScript 字面量

import { parseAsStringLiteral, type inferParserType } from 'nuqs'

// 创建解析器
const parser = parseAsStringLiteral(['asc', 'desc'])

// 可选:提取类型
type SortOrder = inferParserType<typeof parser> // 'asc' | 'desc'

我应该在解析器内联声明值还是在外部?

这取决于®。在解析器内联声明更简短,并使解析器 成为使用 inferParserType 进行类型推断的真实来源, 但它会将值锁定在解析器内部。

在外部声明允许在运行时读取和迭代这些值。 不过,不要忘记添加 as const,否则类型会扩展为 string

字符串字面量

import { parseAsStringLiteral } from 'nuqs'

// 列出接受的值
const sortOrder = ['asc', 'desc'] as const

// 然后将其传递给解析器
parseAsStringLiteral(sortOrder)
Demo loading...

数字字面量

import { parseAsNumberLiteral } from 'nuqs'

parseAsNumberLiteral([1, 2, 3, 4, 5, 6])
import { parseAsNumberLiteral } from 'nuqs'

// 列出接受的值
const diceSides = [1, 2, 3, 4, 5, 6] as const

// 然后将其传递给解析器
parseAsNumberLiteral(diceSides)

枚举

字符串枚举比字符串字面量稍显冗长,但 nuqs 支持它们。

enum Direction {
  up = 'UP',
  down = 'DOWN',
  left = 'LEFT',
  right = 'RIGHT'
}

parseAsStringEnum<Direction>(Object.values(Direction))

注意

查询字符串的值将是枚举的,而不是其名称(此处: ?direction=UP)。

日期 & 时间戳

有三种解析器可以给你一个 Date 对象,它们的区别在于 如何将值编码到查询字符串中。

ISO 8601 日期时间

import { parseAsIsoDateTime } from 'nuqs'

ISO 8601 日期

Introduced in version 2.1.0.
import { parseAsIsoDate } from 'nuqs'

日期在不带时区偏移的情况下解析,使其为 UTC 的 00:00:00。

时间戳

自 Unix 纪元以来的毫秒数。

import { parseAsTimestamp } from 'nuqs'

数组

本页上的所有解析器都可以用于解析各自类型的数组。

import { parseAsArrayOf, parseAsInteger } from 'nuqs'

parseAsArrayOf(parseAsInteger)

// 可选:自定义分隔符
parseAsArrayOf(parseAsInteger, ';')

JSON

如果基本类型不够用,你可以将 JSON 编码到查询字符串中。

传递一个 Standard Schema(例如:Zod 模式) 来验证和推断解析数据的类型:

import { parseAsJson } from 'nuqs'
import { z } from 'zod'

const schema = z.object({
  pkg: z.string(),
  version: z.number(),
  worksWith: z.array(z.string())
})

// 这个解析器是一个函数,不要忘记调用它
// 并将你的模式作为参数传递。
const [json, setJson] = useQueryState('json', parseAsJson(schema))

setJson({
  pkg: 'nuqs',
  version: 2,
  worksWith: ['Next.js', 'React', 'Remix', 'React Router', 'and more']
})

使用其他验证库也是可能的:parseAsJson 接受 任何兼容 Standard Schema 的输入(例如:ArkType、Valibot), 或自定义验证函数(例如:Yup、Joi 等):

import { object, string, number } from 'yup';

let userSchema = object({
  name: string().required(),
  age: number().required().positive().integer(),
});

parseAsJson(userSchema.validateSync)

注意

验证函数必须抛出错误或 对于无效数据返回 null。仅支持同步验证。

原生数组

Introduced in version 2.7.0.

如果你想使用原生 URL 格式的数组,像这样重复相同的键多次:

/products?tag=books&tag=tech&tag=design

现在你可以使用 MultiParsersparseAsNativeArrayOf 来以完全类型安全的方式读取和写入这些值。

import { useQueryState, parseAsNativeArrayOf, parseAsInteger } from 'nuqs'

const [projectIds, setProjectIds] = useQueryState(
  'project',
  parseAsNativeArrayOf(parseAsInteger)
)

// ?project=123&project=456 → [123, 456]
Demo loading...

注意:空数组默认值

parseAsNativeArrayOf 有一个内置的空数组默认值(.withDefault([])),这样你就不必处理 null 情况。

.withDefault() 的调用可以链式使用,因此你可以用它来设置自定义默认值。

在服务器端使用解析器

对于可能在 Next.js app router 中导入的共享代码,你应该从 nuqs/server 导入 解析器,以便在服务器和客户端代码中使用它们, 因为它不包含 'use client' 指令。

import { parseAsString } from 'nuqs/server'

nuqs 导入仅在客户端代码中有效,并且在使用函数(如 .withDefault.withOptions) 跨越共享代码时会抛出打包错误。

对于所有其他框架,你可以互换使用它们,因为它们 不关心 'use client' 指令。