【问题标题】:Vue Router: how to cast params as integers instead of strings?Vue路由器:如何将参数转换为整数而不是字符串?
【发布时间】:2018-09-30 03:45:03
【问题描述】:

当我使用浏览器字段输入 URL 时,参数被转换为字符串,而不是整数,例如/user/1 返回{id: "1"}。但是,当使用this.$route.push({}) 时,参数正确地转换为整数{id: 1}

这种行为是有意的吗?如果没有,我该如何解决?

【问题讨论】:

  • 嗯,url 是字符串,js 值是你使用的任何值类型;你确定this.$route.push 无论如何都不会将任何和所有参数转换为 str - 因为它模仿带有 url 的导航
  • push()转换任何类型。我已经使用整数和字符串进行了测试。使用this.$router.push({name:'user', params: { id: 1 }}); 将产生$route.params.id = 1,使用this.$router.push({name:'user', params: { id: "1" }}); 将产生$route.params.id = "1"
  • @birdspider - 是的,但是其他一些框架能够处理这个问题,你可以用很少的额外语法指定一个参数类型

标签: vue.js vue-router


【解决方案1】:

基于@pongi 的出色回答:https://stackoverflow.com/a/63897213 我想出了一个新包: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"
        }
      })
    }
  ]
});

【讨论】:

    【解决方案2】:

    其中许多解决方案对我来说似乎不必要的复杂。

    这是我在项目中所做的 - 请注意,以 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
      }
    }
    

    如果类型强制失败,这可能会使用一些错误处理,但我将把它留给读者作为练习:)

    【讨论】:

      【解决方案3】:

      我更喜欢 Rodener Dajes 的回答,并在组件中而不是在路由定义中处理类型转换和验证:

      props: {
              id: {
                  type: [Number, String],
                  default: 0
              },
          },
      

      原因是它可以让我定义更简单易读的路线:

      {
      path: '/job/:id',
      name: 'Job',
      component: InvoiceJobDetail,
      props: true
      }
      

      【讨论】:

        【解决方案4】:

        似乎 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'}),
        },
        

        【讨论】:

          【解决方案5】:

          我可能迟到了,但这是我的看法。我编写了一个函数,该函数返回一个函数,该函数将路由参数值转换为具有给定类型的同名道具。

          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}),
          }
          

          这只是提示您可以采取哪些措施来解决此处提出的问题,请勿逐字使用!

          【讨论】:

            【解决方案6】:

            你可以在 props 中使用数组来支持这两种类型

            props: { 
                type:[Number,String],
                required:true
            }
            

            【讨论】:

            • 是的,但这实际上并没有根据 OP 的问题转换值,它只接受任何一种类型。
            【解决方案7】:

            您必须自己处理转换任何参数值。在路由对象中定义一个 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 路由器文档这是在功能模式下

            【讨论】:

            • 如果你有很多路线,很遗憾这是重复和过度的。他们应该实现一些语法糖,如/user/:userId<int> 或类似的东西来支持快速类型定义。
            • 编写自己的 fxn。这样做就像shown here.
            • props: ({params}) => ({userId: Number.parseInt(params.userId, 10) || 0})
            • @CodeFinity:谢谢,工作起来就像一个魅力,而且更干净。
            猜你喜欢
            • 1970-01-01
            • 2021-03-20
            • 2017-05-04
            • 2016-01-22
            • 1970-01-01
            • 1970-01-01
            • 2021-07-17
            • 2020-01-25
            • 2023-03-20
            相关资源
            最近更新 更多