摘要:分析一下,原本我們路徑中的固定部分都是構造函數中的參數,動態部分都在參數。但是問題也來了,這樣的路徑也能匹配。于是在構造函數的參數的每一項中我們還要用一個標識來標記這個的這個屬性值是固定的還是不固定的,如果是固定的輸出否則輸出。
項目用的 React 框架。公司有三類人掌握著 URL 生殺大權,產品總監,產品經理,還有 SEO ???特別是產品總監還兼職首席拼寫官,導致 URL 一周一個樣。即使上線了也是如此,告訴他們用戶已經收藏了這個鏈接,不要隨意更改,然而根本勸不動。
初始配置像這樣寫死的配置顯然是不行的,因為代碼中到處充斥著 this.props.router.push("host/meetings/previous" + 1) 這樣的調用。上線頭一周產品總監就干掉了 host ,變成 "meetings/previous", 這樣一來代碼中處處要改動。而且調用 push 的時候,要來回確定路徑有沒有敲錯,路徑中的動態參數有沒有缺少。
我需要一個靈活一點的,能夠給他們隨意折騰的配置,而且要能有友好的提示。那么上 Typescript 吧。
靈活一點現在我需要一個對象,對象有一個方法能夠生成 Route 中 path 的值,然后還有一個方法能生成跳轉操作的路徑。比如 UpcomingMeetingView 這個對應的路由,path 的值是 "upcoming/:index" 而實際跳轉時候的路徑是類似這樣 "/host/upcomging/1" 的字符串。
比較一下兩個值,可以發現,跳轉時候的路徑是和父路徑相關的,也就是這個對象要保存父對象引用。
然后要怎么做友好的提示呢?對于路徑中的參數我們要知道參數名和參數值類型,這就要用到泛型了。
上代碼:
class PathItem再靈活一點{ /** 自身的路徑字符串 */ self: string /** 父路徑引用 */ parent: PathItem
/** 可選的參數,string 表示參數名,boolean 表示是否必選 */ params: [string, boolean][] constructor (self: string, parent?: PathItem , params?: [string, boolean][]) { this.self = self this.parent = parent this.params = params || [] } /** * 返回跳轉路徑字符串 * @param params {P} 路徑中的動態配置參數 */ pushPath (params?: P) { if (this.parent && Object.keys(this.parent.params).some(v => params[v] === undefined)) { console.error(`${this.parent.self} need a params, when called ${this.self}"s pushPath.`) } let path = this.self === "/" ? "/" : this.self + "/" for (let i = 0, len = this.params.length; i < len; i++) { let param = this.params[i] if (!param) { break } if (params) { if (param[1]) { if (params[param[0]] === undefined) { console.error(`miss a required params in path ${this.self}, [${param[0]}], /n/n the params is ${JSON.stringify(params)}`) } } let p = params[param[0]] if (p) { path += params[param[0]] + "/" } } } if (/^//.test(this.self)) { return path } else { return (this.parent ? this.parent.pushPath(params) : "") + path } } /** * 返回 react-route 中要配置的 path 值 */ routePath () { let path = this.self for (let i = 0, len = this.params.length; i < len; i++) { let param = this.params[i] if (!param) { break } if (param[1] === true) { if (param[2] === true) { path += `/${param[0]}` } else { path += `/:${param[0]}` } } else if (param[1] === false) { if (param[2] === true) { path += `(/${param[0]})` } else { path += `(/:${param[0]})` } } } return path } } // 測試一下 const app = new PathItem("app") console.log(app.routePath()) // => "app" console.log(app.pushPath()) // => "app/" // 然后 app 下面有一個子組件 host const host = new PathItem("host", app) // host 下面有一個子組件 upcoming, 這里提供了泛型參數的類型為一個對象,該對象包只含一個 meeting_id 屬性,屬性值必須是 number 類型 const upcoming = new PathItem<{ meeting_id: number, meeting_desc?: string // 可選的參數 }>("upcomings", _host, [ ["meeting_id", true], ["meeting_desc", false] ]) console.log(upcoming.routePath()) // => "upcomings/:meeting_id(/:meeting_desc)" // 這里如果傳入的參數類型與 new upcoming 對象時指定的泛型參數不一致,會報錯 console.log(upcoming.pushPath({ meeting_id: 1 })) // => "host/upcomings/1/" // 傳入可選參數 console.log(upcoming.pushPath({ meeting_id: 1, meeting_desc: "foo" })) // => host/upcomings/1/foo/
上面的代碼基本滿足需求了,但是 pushPath 返回的值后面帶了個 "/", 這個騷后再說。
某天產品說 "host/upcomings/1/foo" 這樣的路徑根本不知道 1 和 foo 代表的是什么意思,要改成 "host/upcomings/meeting_id/1/meeting_desc/foo"。
分析一下,原本我們路徑中的固定部分都是 PathItem 構造函數中的 self 參數,動態部分都在 params 參數。對于這個需求,可以 new 一個下面這種 PathItem 對象
const upcoming_1 = new PathItem<{ meeting_id: string, meeting_id_value: number, meeting_desc?: string, meeting_desc_value?: string }>("upcomings", _host, [ ["meeting_id", true], ["meeting_id_value", true], ["meeting_desc", false], ["meeting_desc_value", false] ]) console.log(upcoming_1.routePath()) // => "upcomings/:meeting_id/:meeting_id_value(/:meeting_desc)(/:meeting_desc_value)"
看上面的輸出,這樣就能匹配 "host/upcomings/meeting_id/1/meeting_desc/foo" 這種路徑了。但是問題也來了, "host/xxxx/meeting_id/1/xxxxxxx/foo" 這樣的路徑也能匹配。于是在構造函數的 params 參數的每一項中我們還要用一個標識來標記這個 params.meeting_id 的這個屬性值是固定的還是不固定的,如果是固定的輸出 /meeting_id 否則輸出 /:meeting_id。
于是改動 routePath 方法為:
/** * 返回 react-route 中要配置的 path 值 */ routePath () { let path = this.self for (let i = 0, len = this.params.length; i < len; i++) { let param = this.params[i] if (!param) { break } if (param[1] === true) { // 參數必選 if (param[2] === true) { // 固定值 path += `/${param[0]}` } else { path += `/:${param[0]}` } } else if (param[1] === false) { // 參數可選 if (param[2] === true) { // 固定值 path += `(/${param[0]})` } else { path += `(/:${param[0]})` } } } return path } // 測試一下 const upcoming_2 = new PathItem<{ meeting_id: string, meeting_id_value: number, meeting_desc?: string, meeting_desc_value?: string }>("upcomings", _host, [ ["meeting_id", true, true], // 元組中的第三個參數為 true,表示這個 meeting_id 是路徑中的固定值 ["meeting_id_value", true, false], // 元組中的第三個參數為 false,表示這個 meeting_id_value 是路徑中的動態參數 ["meeting_desc", false, true], ["meeting_desc_value", false, false] ]) console.log(upcoming_2.routePath()) // => "upcomings/meeting_id/:meeting_id_value(/meeting_desc)(/:meeting_desc_value)"再完善一下
上面的 pushPath 方法返回的字符串末尾還有 "/" 要去掉,一時沒想到好方法,就用公有方法調用私有方法,在私有方法的返回值中去掉好了。
然后再提供一個 pattern 方法返回能測試 location.pathname 是否與 PathItem 的 pushPath() 返回值是否匹配的正則表達式。
class PathItem{ /** 自身的路徑字符串 */ private self: string /** 父路徑引用 */ private parent: PathItem
/** 可選的參數,string 表示參數名,boolean 表示是否必選 */ private params: [string, boolean, boolean][] /** * @param self {string} 自身的路徑字符串 * @param parent {PathItem } 父路徑引用 * @param params {[string, boolean, boolean][]} 可選的參數,string 表示參數名,第一個 boolean 表示參數是否必選,第二個 boolean 表示是路徑還是動態參數 */ constructor (self: string, parent?: PathItem , params?: Array<[string, boolean, boolean]>) { this.self = self this.parent = parent this.params = params || [] } private __pushPath (params?: P) { if (this.parent && Object.keys(this.parent.params).some(v => params[v] === undefined)) { console.error(`${this.parent.self} need a params, when called ${this.self}"s pushPath.`) } let path = this.self === "/" ? "/" : this.self + "/" for (let i = 0, len = this.params.length; i < len; i++) { let param = this.params[i] if (!param) { break } if (params) { if (param[1]) { if (params[param[0]] === undefined) { console.error(`miss a required params in path ${this.self}, [${param[0]}], /n/n the params is ${JSON.stringify(params)}`) } } let p = params[param[0]] if (p) { path += params[param[0]] + "/" } } } if (/^//.test(this.self)) { return path } else { return (this.parent ? this.parent.__pushPath(params) : "") + path } } /** * 返回要跳轉的路徑 * @param params 路由中的配置項 */ pushPath (params?: P) { return this.__pushPath(params).replace(//$/, "") } __pattern () { let pat = this.self === "/" ? "/" : this.self + "(/)?" if (/^//.test(this.self)) { pat = pat.replace(/^//, "") } for (let i = 0, len = this.params.length; i < len; i++) { let param = this.params[i] if (!param) { break } if (param[2]) { // 固定值 if (param[1]) { // 必選 pat += param[0] + "/" } else { pat += "(" + param[0] + "/)?" } } else { // 動態參數 if (param[1]) { // 必選 pat += "(.+)" + "(/)?" } else { pat += "(.+)?(/)?" } } } if (/^//.test(this.self)) { return pat } else { if (this.parent && this.parent.self !== "/") { return this.parent.pattern() + pat } else { return pat } } } /** * 返回與 pushPath() 返回值相匹配的正則表達式 */ pattern () { return new RegExp(this.__pattern().replace(//$/, "")) } /** * 返回 react-route 中要配置的 path 值 */ routePath () { let path = this.self for (let i = 0, len = this.params.length; i < len; i++) { let param = this.params[i] if (!param) { break } if (param[1] === true) { if (param[2] === true) { path += `/${param[0]}` } else { path += `/:${param[0]}` } } else if (param[1] === false) { if (param[2] === true) { path += `(/${param[0]})` } else { path += `(/:${param[0]})` } } } return path } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88851.html
摘要:談談最近使用的來開發項目,感覺確實是爽的飛起,然而總感覺還是少了點什么。注意當前版本依賴的是請不要安裝最新版。同樣的也有這個方法表示在離開路由前執行。會深度優先遍歷整個理由配置來尋找一個與給定的相匹配的路由。配置是建立在之上的。 談談 最近使用的 React + webpack 來開發項目,感覺確實是爽的飛起,然而總感覺還是少了點什么。對,是多頁面,每次請求頁面還要后端路由給你?多不爽...
摘要:但出于隱私方面的原因,對象不再允許腳本訪問已經訪問過的實際。唯一保持使用的功能只有和方法。將當前和加入到中,并用新的和替換當前,不會造成頁面刷新。 阿里 使用過的koa2中間件https://www.jianshu.com/p/c1e... koa-body原理https://blog.csdn.net/sinat_1... 有沒有涉及到Clusterhttp://nodejs.cn/...
摘要:前言最近將公司項目的從版本升到了版本,跟完全不兼容,是一次徹底的重寫。升級過程中踩了不少的坑,也有一些值得分享的點。沒有就會匹配所有路由最后不得不說升級很困難,坑也很多。 前言 最近將公司項目的 react-router 從 v3 版本升到了 v4 版本,react-router v4 跟 v3 完全不兼容,是一次徹底的重寫。這也給升級造成了極大的困難,與其說升級不如說是對 route...
摘要:面試題來源于網絡,看一下高級前端的面試題,可以知道自己和高級前端的差距。 面試題來源于網絡,看一下高級前端的面試題,可以知道自己和高級前端的差距。有些面試題會重復。 使用過的koa2中間件 koa-body原理 介紹自己寫過的中間件 有沒有涉及到Cluster 介紹pm2 master掛了的話pm2怎么處理 如何和MySQL進行通信 React聲明周期及自己的理解 如何...
閱讀 1659·2021-09-26 09:55
閱讀 5270·2021-09-22 15:40
閱讀 2020·2019-08-30 15:53
閱讀 1503·2019-08-30 11:15
閱讀 1720·2019-08-29 15:41
閱讀 1876·2019-08-28 18:13
閱讀 3152·2019-08-26 12:00
閱讀 1677·2019-08-26 10:30