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 从 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 类型肯定了, params
和 query
就能有字段的提示和检查了,<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... .