成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

搞定 PM:天天改 URL,react-router 動態(tài)路由配置

dadong / 1279人閱讀

摘要:分析一下,原本我們路徑中的固定部分都是構(gòu)造函數(shù)中的參數(shù),動態(tài)部分都在參數(shù)。但是問題也來了,這樣的路徑也能匹配。于是在構(gòu)造函數(shù)的參數(shù)的每一項中我們還要用一個標(biāo)識來標(biāo)記這個的這個屬性值是固定的還是不固定的,如果是固定的輸出否則輸出。

項目用的 React 框架。公司有三類人掌握著 URL 生殺大權(quán),產(chǎn)品總監(jiān),產(chǎn)品經(jīng)理,還有 SEO ???特別是產(chǎn)品總監(jiān)還兼職首席拼寫官,導(dǎo)致 URL 一周一個樣。即使上線了也是如此,告訴他們用戶已經(jīng)收藏了這個鏈接,不要隨意更改,然而根本勸不動。

初始配置

  
    
        
        
        
        
      
  
    
  

像這樣寫死的配置顯然是不行的,因為代碼中到處充斥著 this.props.router.push("host/meetings/previous" + 1) 這樣的調(diào)用。上線頭一周產(chǎn)品總監(jiān)就干掉了 host ,變成 "meetings/previous", 這樣一來代碼中處處要改動。而且調(diào)用 push 的時候,要來回確定路徑有沒有敲錯,路徑中的動態(tài)參數(shù)有沒有缺少。

我需要一個靈活一點的,能夠給他們隨意折騰的配置,而且要能有友好的提示。那么上 Typescript 吧。

靈活一點

現(xiàn)在我需要一個對象,對象有一個方法能夠生成 Route 中 path 的值,然后還有一個方法能生成跳轉(zhuǎn)操作的路徑。比如 UpcomingMeetingView 這個對應(yīng)的路由,path 的值是 "upcoming/:index" 而實際跳轉(zhuǎn)時候的路徑是類似這樣 "/host/upcomging/1" 的字符串。

比較一下兩個值,可以發(fā)現(xiàn),跳轉(zhuǎn)時候的路徑是和父路徑相關(guān)的,也就是這個對象要保存父對象引用。
然后要怎么做友好的提示呢?對于路徑中的參數(shù)我們要知道參數(shù)名和參數(shù)值類型,這就要用到泛型了。
上代碼:

class PathItem

{ /** 自身的路徑字符串 */ self: string /** 父路徑引用 */ parent: PathItem /** 可選的參數(shù),string 表示參數(shù)名,boolean 表示是否必選 */ params: [string, boolean][] constructor (self: string, parent?: PathItem, params?: [string, boolean][]) { this.self = self this.parent = parent this.params = params || [] } /** * 返回跳轉(zhuǎn)路徑字符串 * @param params {P} 路徑中的動態(tài)配置參數(shù) */ 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, 這里提供了泛型參數(shù)的類型為一個對象,該對象包只含一個 meeting_id 屬性,屬性值必須是 number 類型 const upcoming = new PathItem<{ meeting_id: number, meeting_desc?: string // 可選的參數(shù) }>("upcomings", _host, [ ["meeting_id", true], ["meeting_desc", false] ]) console.log(upcoming.routePath()) // => "upcomings/:meeting_id(/:meeting_desc)" // 這里如果傳入的參數(shù)類型與 new upcoming 對象時指定的泛型參數(shù)不一致,會報錯 console.log(upcoming.pushPath({ meeting_id: 1 })) // => "host/upcomings/1/" // 傳入可選參數(shù) console.log(upcoming.pushPath({ meeting_id: 1, meeting_desc: "foo" })) // => host/upcomings/1/foo/

再靈活一點

上面的代碼基本滿足需求了,但是 pushPath 返回的值后面帶了個 "/", 這個騷后再說。
某天產(chǎn)品說 "host/upcomings/1/foo" 這樣的路徑根本不知道 1 和 foo 代表的是什么意思,要改成 "host/upcomings/meeting_id/1/meeting_desc/foo"。

分析一下,原本我們路徑中的固定部分都是 PathItem 構(gòu)造函數(shù)中的 self 參數(shù),動態(tài)部分都在 params 參數(shù)。對于這個需求,可以 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" 這樣的路徑也能匹配。于是在構(gòu)造函數(shù)的 params 參數(shù)的每一項中我們還要用一個標(biāo)識來標(biāo)記這個 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) { // 參數(shù)必選
        if (param[2] === true) { // 固定值
          path += `/${param[0]}`
        } else {
          path += `/:${param[0]}`
        }
      } else if (param[1] === false) { // 參數(shù)可選
        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], // 元組中的第三個參數(shù)為 true,表示這個 meeting_id 是路徑中的固定值
  ["meeting_id_value", true, false], // 元組中的第三個參數(shù)為 false,表示這個 meeting_id_value 是路徑中的動態(tài)參數(shù)
  ["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 方法返回的字符串末尾還有 "/" 要去掉,一時沒想到好方法,就用公有方法調(diào)用私有方法,在私有方法的返回值中去掉好了。
然后再提供一個 pattern 方法返回能測試 location.pathname 是否與 PathItem 的 pushPath() 返回值是否匹配的正則表達式。

class PathItem

{ /** 自身的路徑字符串 */ private self: string /** 父路徑引用 */ private parent: PathItem /** 可選的參數(shù),string 表示參數(shù)名,boolean 表示是否必選 */ private params: [string, boolean, boolean][] /** * @param self {string} 自身的路徑字符串 * @param parent {PathItem} 父路徑引用 * @param params {[string, boolean, boolean][]} 可選的參數(shù),string 表示參數(shù)名,第一個 boolean 表示參數(shù)是否必選,第二個 boolean 表示是路徑還是動態(tài)參數(shù) */ 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 } } /** * 返回要跳轉(zhuǎn)的路徑 * @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 { // 動態(tài)參數(shù) 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 } }

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/88851.html

相關(guān)文章

  • 談?wù)?react-router

    摘要:談?wù)勛罱褂玫膩黹_發(fā)項目,感覺確實是爽的飛起,然而總感覺還是少了點什么。注意當(dāng)前版本依賴的是請不要安裝最新版。同樣的也有這個方法表示在離開路由前執(zhí)行。會深度優(yōu)先遍歷整個理由配置來尋找一個與給定的相匹配的路由。配置是建立在之上的。 談?wù)?最近使用的 React + webpack 來開發(fā)項目,感覺確實是爽的飛起,然而總感覺還是少了點什么。對,是多頁面,每次請求頁面還要后端路由給你?多不爽...

    MASAILA 評論0 收藏0
  • 大廠高級前端面試題答案

    摘要:但出于隱私方面的原因,對象不再允許腳本訪問已經(jīng)訪問過的實際。唯一保持使用的功能只有和方法。將當(dāng)前和加入到中,并用新的和替換當(dāng)前,不會造成頁面刷新。 阿里 使用過的koa2中間件https://www.jianshu.com/p/c1e... koa-body原理https://blog.csdn.net/sinat_1... 有沒有涉及到Clusterhttp://nodejs.cn/...

    oysun 評論0 收藏0
  • react-router 升級小記

    摘要:前言最近將公司項目的從版本升到了版本,跟完全不兼容,是一次徹底的重寫。升級過程中踩了不少的坑,也有一些值得分享的點。沒有就會匹配所有路由最后不得不說升級很困難,坑也很多。 前言 最近將公司項目的 react-router 從 v3 版本升到了 v4 版本,react-router v4 跟 v3 完全不兼容,是一次徹底的重寫。這也給升級造成了極大的困難,與其說升級不如說是對 route...

    isLishude 評論0 收藏0
  • 高級前端面試題大匯總(只有試題,沒有答案)

    摘要:面試題來源于網(wǎng)絡(luò),看一下高級前端的面試題,可以知道自己和高級前端的差距。 面試題來源于網(wǎng)絡(luò),看一下高級前端的面試題,可以知道自己和高級前端的差距。有些面試題會重復(fù)。 使用過的koa2中間件 koa-body原理 介紹自己寫過的中間件 有沒有涉及到Cluster 介紹pm2 master掛了的話pm2怎么處理 如何和MySQL進行通信 React聲明周期及自己的理解 如何...

    kviccn 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<