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