默认情况下,TanStack Router 会自动使用 JSON.stringify 和 JSON.parse 来解析和序列化您的 URL 搜索参数。这个过程涉及对搜索字符串进行转义和反转义,这是 URL 搜索参数的常见做法,此外还包括搜索对象的序列化和反序列化。
例如,使用默认配置,如果您有以下搜索对象
const search = {
page: 1,
sort: 'asc',
filters: { author: 'tanner', min_words: 800 },
}
const search = {
page: 1,
sort: 'asc',
filters: { author: 'tanner', min_words: 800 },
}
它将被序列化并转义为以下搜索字符串
?page=1&sort=asc&filters=%7B%22author%22%3A%22tanner%22%2C%22min_words%22%3A800%7D
?page=1&sort=asc&filters=%7B%22author%22%3A%22tanner%22%2C%22min_words%22%3A800%7D
我们可以用以下代码来实现默认行为
import {
createRouter,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
const router = createRouter({
// ...
parseSearch: parseSearchWith(JSON.parse),
stringifySearch: stringifySearchWith(JSON.stringify),
})
import {
createRouter,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
const router = createRouter({
// ...
parseSearch: parseSearchWith(JSON.parse),
stringifySearch: stringifySearchWith(JSON.stringify),
})
然而,这种默认行为可能不适用于所有用例。例如,您可能希望使用不同的序列化格式,如 base64 编码,或者您可能希望使用专门的序列化/反序列化库,例如 query-string、JSURL2 或 Zipson。
这可以通过在 Router 配置中为 parseSearch 和 stringifySearch 选项提供自己的序列化和反序列化函数来实现。这样做时,您可以使用 TanStack Router 内置的辅助函数 parseSearchWith 和 stringifySearchWith 来简化这个过程。
提示
序列化和反序列化一个重要的方面是,您能够在反序列化后获得相同的对象。这一点很重要,因为如果序列化和反序列化过程没有正确完成,您可能会丢失一些信息。例如,如果您使用的是不支持嵌套对象的库,那么在反序列化搜索字符串时,您可能会丢失嵌套对象。
以下是一些关于如何自定义 TanStack Router 中搜索参数序列化的示例
通常,我们会对搜索参数进行 base64 编码,以实现跨浏览器和 URL 展开等的最大兼容性。这可以通过以下代码完成
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
const router = createRouter({
parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(JSON.stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
const router = createRouter({
parseSearch: parseSearchWith((value) => JSON.parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(JSON.stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
因此,如果我们使用此配置将前面的对象转换为搜索字符串,它将如下所示
?page=1&sort=asc&filters=eyJhdXRob3IiOiJ0YW5uZXIiLCJtaW5fd29yZHMiOjgwMH0%3D
?page=1&sort=asc&filters=eyJhdXRob3IiOiJ0YW5uZXIiLCJtaW5fd29yZHMiOjgwMH0%3D
警告
如果您将用户输入序列化为 Base64,您可能会导致与 URL 反序列化发生冲突。这可能导致意外行为,例如 URL 未能正确解析或被解释为不同的值。为避免这种情况,您应该使用安全的二进制编码/解码方法(见下文)来编码搜索参数。
query-string 库因其能够可靠地解析和字符串化查询字符串而广受欢迎。您可以使用它来定制搜索参数的序列化格式。这可以通过以下代码完成
import { createRouter } from '@tanstack/solid-router'
import qs from 'query-string'
const router = createRouter({
// ...
stringifySearch: stringifySearchWith((value) =>
qs.stringify(value, {
// ...options
}),
),
parseSearch: parseSearchWith((value) =>
qs.parse(value, {
// ...options
}),
),
})
import { createRouter } from '@tanstack/solid-router'
import qs from 'query-string'
const router = createRouter({
// ...
stringifySearch: stringifySearchWith((value) =>
qs.stringify(value, {
// ...options
}),
),
parseSearch: parseSearchWith((value) =>
qs.parse(value, {
// ...options
}),
),
})
因此,如果我们使用此配置将前面的对象转换为搜索字符串,它将如下所示
?page=1&sort=asc&filters=author%3Dtanner%26min_words%3D800
?page=1&sort=asc&filters=author%3Dtanner%26min_words%3D800
JSURL2 是一个非标准的库,可以压缩 URL,同时保持可读性。这可以通过以下代码完成
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
import { parse, stringify } from 'jsurl2'
const router = createRouter({
// ...
parseSearch: parseSearchWith(parse),
stringifySearch: stringifySearchWith(stringify),
})
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
import { parse, stringify } from 'jsurl2'
const router = createRouter({
// ...
parseSearch: parseSearchWith(parse),
stringifySearch: stringifySearchWith(stringify),
})
因此,如果我们使用此配置将前面的对象转换为搜索字符串,它将如下所示
?page=1&sort=asc&filters=(author~tanner~min*_words~800)~
?page=1&sort=asc&filters=(author~tanner~min*_words~800)~
Zipson 是一个非常用户友好且高性能的 JSON 压缩库(在运行时性能和最终压缩性能方面)。要使用它来压缩您的搜索参数(这还需要对它们进行转义/反转义以及 base64 编码/解码),您可以使用以下代码
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
import { stringify, parse } from 'zipson'
const router = createRouter({
parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
import {
Router,
parseSearchWith,
stringifySearchWith,
} from '@tanstack/solid-router'
import { stringify, parse } from 'zipson'
const router = createRouter({
parseSearch: parseSearchWith((value) => parse(decodeFromBinary(value))),
stringifySearch: stringifySearchWith((value) =>
encodeToBinary(stringify(value)),
),
})
function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
因此,如果我们使用此配置将前面的对象转换为搜索字符串,它将如下所示
?page=1&sort=asc&filters=JTdCJUMyJUE4YXV0aG9yJUMyJUE4JUMyJUE4dGFubmVyJUMyJUE4JUMyJUE4bWluX3dvcmRzJUMyJUE4JUMyJUEyQ3UlN0Q%3D
?page=1&sort=asc&filters=JTdCJUMyJUE4YXV0aG9yJUMyJUE4JUMyJUE4dGFubmVyJUMyJUE4JUMyJUE4bWluX3dvcmRzJUMyJUE4JUMyJUEyQ3UlN0Q%3D
在浏览器中,atob 和 btoa 函数不能保证正确处理非 UTF8 字符。我们建议改用这些编码/解码实用程序
从字符串编码到二进制字符串
export function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
export function encodeToBinary(str: string): string {
return btoa(
encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode(parseInt(p1, 16))
}),
)
}
从二进制字符串解码到字符串
export function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
export function decodeFromBinary(str: string): string {
return decodeURIComponent(
Array.prototype.map
.call(atob(str), function (c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
})
.join(''),
)
}
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。