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... .

相關文章
相關標籤/搜索