Vue Router:如何将参数转换为整数而不是字符串?
Vue Router: how to cast params as integers instead of strings?
当我使用浏览器字段输入 URL 时,参数被转换为字符串,而不是整数,例如/user/1
returns {id: "1"}
。但是,当使用 this.$route.push({})
时,参数会正确地转换为整数 {id: 1}
。
这种行为是故意的吗?如果没有,我该如何解决?
您必须自己处理任何参数值的转换。在路由对象中定义一个 props 函数。这是一个例子:
{
path: '/user/:userId',
component: UserProfile,
props: (route) => {
const userId = Number.parseInt(route.params.userId, 10)
if (Number.isNaN(userId)) {
return 0
}
return { userId }
}
}
link vue router docs 这是在函数模式下
你可以在 props 中使用数组来支持这两种类型
props: {
type:[Number,String],
required:true
}
我可能迟到了,但这是我的看法。我写了一个函数,该函数 returns 一个将路由参数值转换为具有给定类型的同名道具的函数。
function paramsToPropsCaster(mapping) {
return function(route) {
let nameType = Object.entries(mapping); // [[param1, Number], [param2, String]]
let nameRouteParam = nameType.map(([name, fn]) => [name, fn(route.params[name])]); // [[param1, 1], [param2, "hello"]]
let props = Object.fromEntries(nameRouteParam); // {param1: 1, param2: "hello"}
return props;
}
}
然后,在您的路线定义中:
{
path: '/projects/:param1/editor/:param2',
component: ProjectEditor,
name: 'project-editor',
props: paramsToPropsCaster({'param1': Number, 'param2': String}),
}
这只是一个提示,提示您如何解决此处提出的问题,请勿逐字使用!
Vue Router 似乎没有为此提供快捷方式,所以我想出了我自己的。下面的 castParams
函数生成一个 props 函数,该函数内置了指定的类型转换。我已经为整数和布尔值添加了转换,但您可以轻松地将其扩展为您想要转换为的任何其他类型。
// casts should be an object where the keys are params that might appear in the route, and the values specify how to cast the parameters
const castParams = (casts) => {
return (route) => {
const props = {};
for (var key in route.params) {
const rawValue = route.params[key];
const cast = casts[key];
if (rawValue == null) {
// Don't attempt to cast null or undefined values
props[key] = rawValue;
} else if (cast == null) {
// No cast specified for this parameter
props[key] = rawValue;
} else if (cast == 'integer') {
// Try to cast this parameter as an integer
const castValue = Number.parseInt(rawValue, 10);
props[key] = isNaN(castValue) ? rawValue : castValue;
} else if (cast == 'boolean') {
// Try to cast this parameter as a boolean
if (rawValue === 'true' || rawValue === '1') {
props[key] = true;
} else if (rawValue === 'false' || rawValue === '0') {
props[key] = false;
} else {
props[key] = rawValue;
}
} else if (typeof(cast) == 'function') {
// Use the supplied function to cast this param
props[key] = cast(rawValue);
} else {
console.log("Unexpected route param cast", cast);
props[key] = rawValue;
}
}
return props;
};
};
然后你可以在你的路由定义中使用它,例如:
{
path: '/contact/:contactId',
component: 'contact-details-page',
props: castParams({contactId: 'integer'}),
},
我确实更喜欢 Rodener Dajes 的回答,并在组件内而不是在路由定义中处理类型转换和验证:
props: {
id: {
type: [Number, String],
default: 0
},
},
原因是它可以让我定义更简单和可读的路线:
{
path: '/job/:id',
name: 'Job',
component: InvoiceJobDetail,
props: true
}
其中许多解决方案对我来说似乎不必要的复杂。
这是我在我的项目中所做的 - 请注意,以 ID
结尾的路由参数或参数 id
本身会自动转换为 Number
,所以在我的情况下,我只是我几乎所有的路线都必须设置 props: typedProps(),
。
/**
* Casts props into proper data types.
* Props ending in 'ID' and the prop 'id' are cast to Number automatically.
* To cast other props or override the defaults, pass a mapping like this:
* @example
* // Truthy values like 'true', 'yes', 'on' and '1' are converted to Boolean(true)
* {
* path: '/:isNice/:age/:hatSize',
* name: 'foo route',
* props: typedProps({ isNice: Boolean, age: Number, hatSize: Number}),
* },
* @param {Object} mapping
* @returns
*/
const typedProps = (mapping) => {
if (!mapping) {
mapping = {}
}
return (route) => {
let props = {}
for (let [prop, value] of Object.entries(route.params)) {
if (prop in mapping) {
if (mapping[prop] === Boolean) {
value = ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
} else {
value = mapping[prop](value)
}
} else if (prop === 'id' || prop.endsWith('ID')) {
value = Number(value)
}
props[prop] = value
}
return props
}
}
如果类型强制失败,这可以使用一些错误处理,但我将把它留作 reader :)
的练习
基于@pongi 的出色回答: I came up with a new package: https://www.npmjs.com/package/vue-router-parse-props。它是用打字稿写的并且有类型。请告诉我您的想法。
npm i vue-router-parse-props
// src/router/index.ts
import propsParser from 'vue-router-parse-props'
import { parse } from 'date-fns'
const router = new Router({
base: process.env.BASE_URL,
mode: useHistory ? "history" : "hash",
routes: [
{
path: ':day/:userId',
name: 'UserProfile',
component: () => import('@/components/UserProfile.vue'),
props: paramsToPropsCaster({
userId: Number,
day: (val: string): Date => parse(val, 'yyyy-MM-dd', new Date()),
searchId: {
type: id,
routeKey: "query.q"
}
})
}
]
});
当我使用浏览器字段输入 URL 时,参数被转换为字符串,而不是整数,例如/user/1
returns {id: "1"}
。但是,当使用 this.$route.push({})
时,参数会正确地转换为整数 {id: 1}
。
这种行为是故意的吗?如果没有,我该如何解决?
您必须自己处理任何参数值的转换。在路由对象中定义一个 props 函数。这是一个例子:
{
path: '/user/:userId',
component: UserProfile,
props: (route) => {
const userId = Number.parseInt(route.params.userId, 10)
if (Number.isNaN(userId)) {
return 0
}
return { userId }
}
}
link vue router docs 这是在函数模式下
你可以在 props 中使用数组来支持这两种类型
props: {
type:[Number,String],
required:true
}
我可能迟到了,但这是我的看法。我写了一个函数,该函数 returns 一个将路由参数值转换为具有给定类型的同名道具的函数。
function paramsToPropsCaster(mapping) {
return function(route) {
let nameType = Object.entries(mapping); // [[param1, Number], [param2, String]]
let nameRouteParam = nameType.map(([name, fn]) => [name, fn(route.params[name])]); // [[param1, 1], [param2, "hello"]]
let props = Object.fromEntries(nameRouteParam); // {param1: 1, param2: "hello"}
return props;
}
}
然后,在您的路线定义中:
{
path: '/projects/:param1/editor/:param2',
component: ProjectEditor,
name: 'project-editor',
props: paramsToPropsCaster({'param1': Number, 'param2': String}),
}
这只是一个提示,提示您如何解决此处提出的问题,请勿逐字使用!
Vue Router 似乎没有为此提供快捷方式,所以我想出了我自己的。下面的 castParams
函数生成一个 props 函数,该函数内置了指定的类型转换。我已经为整数和布尔值添加了转换,但您可以轻松地将其扩展为您想要转换为的任何其他类型。
// casts should be an object where the keys are params that might appear in the route, and the values specify how to cast the parameters
const castParams = (casts) => {
return (route) => {
const props = {};
for (var key in route.params) {
const rawValue = route.params[key];
const cast = casts[key];
if (rawValue == null) {
// Don't attempt to cast null or undefined values
props[key] = rawValue;
} else if (cast == null) {
// No cast specified for this parameter
props[key] = rawValue;
} else if (cast == 'integer') {
// Try to cast this parameter as an integer
const castValue = Number.parseInt(rawValue, 10);
props[key] = isNaN(castValue) ? rawValue : castValue;
} else if (cast == 'boolean') {
// Try to cast this parameter as a boolean
if (rawValue === 'true' || rawValue === '1') {
props[key] = true;
} else if (rawValue === 'false' || rawValue === '0') {
props[key] = false;
} else {
props[key] = rawValue;
}
} else if (typeof(cast) == 'function') {
// Use the supplied function to cast this param
props[key] = cast(rawValue);
} else {
console.log("Unexpected route param cast", cast);
props[key] = rawValue;
}
}
return props;
};
};
然后你可以在你的路由定义中使用它,例如:
{
path: '/contact/:contactId',
component: 'contact-details-page',
props: castParams({contactId: 'integer'}),
},
我确实更喜欢 Rodener Dajes 的回答,并在组件内而不是在路由定义中处理类型转换和验证:
props: {
id: {
type: [Number, String],
default: 0
},
},
原因是它可以让我定义更简单和可读的路线:
{
path: '/job/:id',
name: 'Job',
component: InvoiceJobDetail,
props: true
}
其中许多解决方案对我来说似乎不必要的复杂。
这是我在我的项目中所做的 - 请注意,以 ID
结尾的路由参数或参数 id
本身会自动转换为 Number
,所以在我的情况下,我只是我几乎所有的路线都必须设置 props: typedProps(),
。
/**
* Casts props into proper data types.
* Props ending in 'ID' and the prop 'id' are cast to Number automatically.
* To cast other props or override the defaults, pass a mapping like this:
* @example
* // Truthy values like 'true', 'yes', 'on' and '1' are converted to Boolean(true)
* {
* path: '/:isNice/:age/:hatSize',
* name: 'foo route',
* props: typedProps({ isNice: Boolean, age: Number, hatSize: Number}),
* },
* @param {Object} mapping
* @returns
*/
const typedProps = (mapping) => {
if (!mapping) {
mapping = {}
}
return (route) => {
let props = {}
for (let [prop, value] of Object.entries(route.params)) {
if (prop in mapping) {
if (mapping[prop] === Boolean) {
value = ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
} else {
value = mapping[prop](value)
}
} else if (prop === 'id' || prop.endsWith('ID')) {
value = Number(value)
}
props[prop] = value
}
return props
}
}
如果类型强制失败,这可以使用一些错误处理,但我将把它留作 reader :)
的练习基于@pongi 的出色回答:
npm i vue-router-parse-props
// src/router/index.ts
import propsParser from 'vue-router-parse-props'
import { parse } from 'date-fns'
const router = new Router({
base: process.env.BASE_URL,
mode: useHistory ? "history" : "hash",
routes: [
{
path: ':day/:userId',
name: 'UserProfile',
component: () => import('@/components/UserProfile.vue'),
props: paramsToPropsCaster({
userId: Number,
day: (val: string): Date => parse(val, 'yyyy-MM-dd', new Date()),
searchId: {
type: id,
routeKey: "query.q"
}
})
}
]
});