ruled-router 生成路由类型细节记录

ruled-router 是咱们(积梦前端)定制的路由方案, 另外强化了类型方面,
以前的介绍能够看文章: 积梦前端的路由方案 ruled-router.前端

关于跳转方法的类型

路由生成部分, 大体上就是对于规则:git

[
  {
    "name": "a",
    "path": "a",
    "next": [
      {
        "name": "b",
        "path": "b/:id"
      }
    ]
  }
]

会经过脚本生成路由的调用方法, 如今的生成结果是:github

export let genRouter = {
  a: {
    name: "a",
    raw: "a",
    path: () => `/a`,
    go: () => switchPath(`/a`),
    b: {
      name: "b",
      raw: "b",
      path: (id: string) => `/a/b/${id}`,
      go: (id: string) => switchPath(`/a/b/${id}`),
    },
  },
};

这样能够经过调用方法来进行路由跳转,json

genRouter.a.b.go(id)

这个步骤, 是有类型支持的. TypeScript 会检查整个结构, 不会有错误的调用.
也就是说, 全部的调用, 按照这个写法, 不会致使出现不符合路由规则的路径.
整个实现模块维护在 https://github.com/jimengio/r... .segmentfault

解析结果的类型问题

如今的短板是在解析解析结果的类型上面, 回顾一下 ruled-router 解析的结果,
对于路径:工具

/home/plant/123/shop/456/789

按照路由规则作一次解析,优化

let pageRules = [
  {
    path: "home",
    next: [
      {
        path: "plant/:plantId",
        next: [
          {
            path: "shop/:shopId/:corner"
          }
        ]
      }
    ]
  }
];

会获得一个 JSON 结构,this

{
  "raw": "home",
  "name": "home",
  "matches": true,
  "restPath": ["plant", "123", "shop", "456", "789"],
  "params": {},
  "data": {},
  "next": {
    "raw": "plant/:plantId",
    "name": "plant",
    "matches": true,
    "restPath": ["shop", "456", "789"],
    "params": {
      "plantId": "123"
    },
    "data": {
      "plantId": "123"
    },
    "next": {
      "raw": "shop/:shopId/:corner",
      "name": "shop",
      "matches": true,
      "next": null,
      "restPath": [],
      "data": {
        "shopId": "456",
        "corner": "789"
      },
      "params": {
        "plantId": "123",
        "shopId": "456",
        "corner": "789"
      }
    }
  }
}

这个 JSON 结构当中部分字段是固定的, 部分是按照规则定义的参数,
若是用一个类型来表示, 就是:3d

interface IParsedResult<IParams, IQuery>

这也是咱们以往的写法. 这个写法比较稳妥, 可是问题就是书写麻烦,
路由比较多, 须要手写的 IParams IQuery 比较多, 也难以维护.rest

当前尝试生成路由的方案

对于这个问题, 我想到的方案, 主要是能不能像前面同样把类型都生成出来,
大体想到的是这样一个方案, 生成一棵嵌套的路由的树,
https://gist.github.com/cheny...
我须要这棵树知足两个需求,

  • 能获得一个完整的路由, 其中的 next: A | B | C 能罗列全部子路由类型,
  • 我能经过 x.y.z.$type 来获取其中一棵子树, 由于子组件须要具体一个类型,

这个方案最重要的地方就是须要 VS Code 能推断出类型进行提示,
通过调整之后, 获得一个可用的方案, 基于这样的规则,

[
  {
    "path": "a",
    "queries": ["a"],
    "next": [
      {
        "path": "b",
        "queries": ["a", "b"]
      },
      {
        "path": "d"
      }
    ]
  }
]

生成的类型文件的是这样:

export type GenRouterTypeMain = GenRouterTypeTree["a"];

export interface GenRouterTypeTree {
  a: {
    name: "a";
    params: {};
    query: { a: string };
    next: GenRouterTypeTree["a"]["b"] | GenRouterTypeTree["a"]["d"];
    b: {
      name: "b";
      params: {};
      query: { a: string; b: string };
      next: null;
    };
    d: {
      name: "d";
      params: {};
      query: { a: string };
      next: null;
    };
  };
}
  • 顶层的路由

页面首先会被解析, 获得一个 router 对象

let router: GenRouterTypeMain = parseRoutePath(this.props.location.pathname, pageRules);

router 的类型是 GenRouterTypeMain, 这个类型是顶层的类型,
这个例子当中只有一个顶级路由,

export type GenRouterTypeMain = GenRouterTypeTree["a"];

实际当中更多是多个可选值, 就像这样

type GenRouterTypeMain = GenRouterTypeTree["a"] | GenRouterTypeTree["b"] | GenRouterTypeTree["c"];
  • 组件使用的子路由

子组件当中, props.router 的类型对应的是子树的某一个位置,
这里的 next 由于用了 Union Type, 不能直接引用其中某个 case,
就须要经过另外一个写法, 从数据的路径上直接经过类型访问, 好比:

GenRouterTypeTree["a"]

更深层的子组件的类型, 好比嵌套的第二层, 就须要用:

GenRouterTypeTree["a"]["b"]

不过这个在组件定义当中并不直接是拿到, 由于在 props 可能没法肯定类型,
就须要经过父级的 next 来访问, 具体是一个 Union Type:

let InformationIndex: FC<{
  router: GenRouterTypeTree["a"]["next"] }
  // next type
  // GenRouterTypeTree["a"]["b"] | GenRouterTypeTree["a"]["d"]

> = (props) => {
  // TODO
}
  • 配合 VS Code 作类型推断

为了能让 VS Code 从 next 推断出类型, 须要同 switch 语句判断,

if (props.router) {
  switch (props.router.name) {
  case "b": // TODO, router: GenRouterTypeTree["a"]["b"]
  case "d": // TODO, router: GenRouterTypeTree["a"]["d"]
  }
}

效果大体上,

  • case 后面的字符串在必定程度上能够自动补全和类型检查,
  • case 后面, router 类型肯定了, paramsquery 就能有字段的提示和检查了,
  • 若是内部有子组件 <A router={router.next} />, router.next 会被类型检查.

固然这些主要仍是提示的做用, 并非彻底跟 router 对应的类型, 否则结构会更复杂,
我试着在已有的组件项目当中作了尝试, 包括比连接更大的项目, 基本是可用的,
https://github.com/jimengio/m...

其余

目前来讲, 能对项目路由进行检查, 就算是达到了最初的类型的目标,
至少可以保证, 开发当中, 使用生成的路由, 能提示和检查 params query 中的字段,
而且提交到仓库的代码, CI 当中能检查到参数, 作一些质量的保证.

case 当中可以提示字符串, 算是意料以外的一个好处吧.
不过这个也要注意, VS Code 推断的能力有限, 只能用 switch 这个简单的写法,
再复杂一些, 好比嵌套了表达式, 或者往子路由数据再判断, 就推断不出来了.

当前比较担忧的是项目当中出现深度嵌套的路由, 加上字段名称长, 总体会很是长:

GenRouterTypeTree["a"]["d"]["e"]["f"]["g"]

因为咱们最大的项目当中曾在深达 6 层的路由, 不能不担忧会出现超长的单行路由...
后面再想一想有没有什么办法继续作优化..


其余关于积梦前端的模块和工具能够查看咱们的 GitHub 主页 https://github.com/jimengio .
目前团队正在扩充, 招聘文档见 GitHub 仓库 https://github.com/jimengio/h... .

相关文章
相关标签/搜索