成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

dadong / 1330人閱讀

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

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

初始配置

  
    
        
        
        
        
      
  
    
  

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

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

靈活一點(diǎn)

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

比較一下兩個(gè)值,可以發(fā)現(xiàn),跳轉(zhuǎn)時(shí)候的路徑是和父路徑相關(guān)的,也就是這個(gè)對象要保存父對象引用。
然后要怎么做友好的提示呢?對于路徑中的參數(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} 路徑中的動(dòng)態(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 下面有一個(gè)子組件 host const host = new PathItem("host", app) // host 下面有一個(gè)子組件 upcoming, 這里提供了泛型參數(shù)的類型為一個(gè)對象,該對象包只含一個(gè) 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í)指定的泛型參數(shù)不一致,會(huì)報(bào)錯(cuò) 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/

再靈活一點(diǎn)

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

分析一下,原本我們路徑中的固定部分都是 PathItem 構(gòu)造函數(shù)中的 self 參數(shù),動(dòng)態(tài)部分都在 params 參數(shù)。對于這個(gè)需求,可以 new 一個(gè)下面這種 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ù)的每一項(xiàng)中我們還要用一個(gè)標(biāo)識(shí)來標(biāo)記這個(gè) params.meeting_id 的這個(gè)屬性值是固定的還是不固定的,如果是固定的輸出 /meeting_id 否則輸出 /:meeting_id。

于是改動(dòng) 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], // 元組中的第三個(gè)參數(shù)為 true,表示這個(gè) meeting_id 是路徑中的固定值
  ["meeting_id_value", true, false], // 元組中的第三個(gè)參數(shù)為 false,表示這個(gè) meeting_id_value 是路徑中的動(dòng)態(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 方法返回的字符串末尾還有 "/" 要去掉,一時(shí)沒想到好方法,就用公有方法調(diào)用私有方法,在私有方法的返回值中去掉好了。
然后再提供一個(gè) pattern 方法返回能測試 location.pathname 是否與 PathItem 的 pushPath() 返回值是否匹配的正則表達(dá)式。

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ù)名,第一個(gè) boolean 表示參數(shù)是否必選,第二個(gè) boolean 表示是路徑還是動(dòng)態(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 路由中的配置項(xiàng) */ 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 { // 動(dòng)態(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() 返回值相匹配的正則表達(dá)式 */ 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://m.hztianpu.com/yun/88851.html

相關(guān)文章

  • 談?wù)?react-router

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

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

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

    oysun 評論0 收藏0
  • react-router 升級(jí)小記

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

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

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

    kviccn 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<