react-router升級到4以後,跟前面版本比有了很大的差異。
例如包的拆分,動態路由等詳細的差異就不說了,各位大神的總結也很到位,詳細能夠點擊看看,All About React Router 4這篇文章。
此外還有個差異是路由規則的變化。 一直有着上個版本的習慣,因此稍微複雜的路由,配起來的時候簡直痛不欲生。
痛定思痛,要好好了解下其依賴的匹配規則,即path-to-regexp。css
本文指望讀者是對react-router有過使用的同窗,否則本文省略了太多東西,可能看起來可能有點太亂。html
其文檔一句話介紹很簡潔明瞭: 將路徑字符串(如/user/:name)轉換爲正則表達式。react-router matchPath就是基於其來匹配了。react
var pathToRegexp = require('path-to-regexp')
// pathToRegexp(path, keys?, options?)
// pathToRegexp.parse(path)
// pathToRegexp.compile(path)
複製代碼
參數:git
仍是直接看官方例子吧github
// 匹配的path中關鍵字,獲得由其組成的數組
// 簡而言之,就是匹配的結果,增長該參數,能夠更方便的使用和分析
var keys = []
var re = pathToRegexp('/foo/:bar', keys)
// 執行結果,轉換以後的正則就以下
// re = /^\/foo\/([^\/]+?)\/?$/i
// 獲得的路由相關信息
keys = [
{
// 路由path中的參數名稱
name: 'bar',
// 前綴,分隔符等
prefix: '/',
delimiter: '/',
optional: false,
repeat: false,
pattern: '[^\\/]+?'
}
]
複製代碼
這樣看起來應該清楚一下,下面繼續看使用規則正則表達式
最簡單的例子(結合react-router-config 路由最簡單的路由能夠以下, 各字段含義就不提了,本文只關注匹配規則):數組
const routes = [
{ component: Root,
routes: [
{
//只匹配/
path: '/',
exact: true,
component: Home
}
]
}
]
複製代碼
看起來也不過爾爾,簡單匹配就完了,可是若是要是有比較複雜的路徑的話,例若有這麼一個路徑:'/a/1/3.html' 其實/1/3都是能夠省略的也是可選的,也就是說以下面這樣:react-router
'/a/1/3.html'
'/a.html'
'/a/2.html'
複製代碼
先不要急着寫,這種固然是要有按照相應規則來匹配了,先看下對應規則:工具
路徑參數將會被用來定義參數和匹配關鍵字列表(即咱們的keys)ui
命名參數經過以下形式定義: 在參數前面加上引號,例如:‘:foo’。默認狀況下,在path的該區域結束以前的部分都會被匹配到(默認的話也就是兩個//之間爲一個區域,例如/:foo/,那麼:foo 部分就是一個區域(segment))。
var re = pathToRegexp('/:foo/:bar')
// 對應的匹配key數組以下
keys = [
{ name: 'foo', prefix: '/', ... },
{ name: 'bar', prefix: '/', ... }
]
// 對於下面的path,執行結果
re.exec('/test/route')
//=> ['/test/route', 'test', 'route']
複製代碼
參數後綴能夠加上一個可選標識即'?',代表該參數可選,這樣狀況下該部分參數若是沒有也不正確匹配,只不過在匹配結果裏值爲undefined
var re = pathToRegexp('/:foo/:bar?')
keys = [
{ name: 'foo', ... },
{
name: 'bar',
delimiter: '/',
// 匹配key數組第二部分就爲true,代表該參數可選
optional: true,
repeat: false }
]
// 可省略候選bar對應的部分
re.exec('/test')
//=> ['/test', 'test', undefined]
re.exec('/test/route')
//=> ['/test', 'test', 'route']
複製代碼
固然參數能夠以*結尾,標識該部分參數0-n(能夠類比正則)。每一個匹配都會將前綴(/)考慮進去,即/已經不是默認的區塊分割了,這也是跟?的區別。看例子比較清晰
var re = pathToRegexp('/:foo*')
// keys = [{ name: 'foo', delimiter: '/', optional: true, repeat: true }]
re.exec('/')
//=> ['/', undefined]
// 主要看這裏,這時候/baz的內容一樣被當成 foo的value組成部分了,直接和前面的一塊兒輸出
re.exec('/bar/baz')
//=> ['/bar/baz', 'bar/baz']
複製代碼
對比下?修飾符,應該比較清楚了。
var re = pathToRegexp('/:foo?')
// 直接認爲是不匹配的,輸出爲null
re.exec('/bar/baz')
//=> null
複製代碼
參數以+結尾時,代表該部分參數至少爲1,一樣會將分隔符計算進來。能夠對比下上面與*的區別
var re = pathToRegexp('/:foo+')
// keys = [{ name: 'foo', delimiter: '/', optional: false, repeat: true }]
// 此時/ 的路由已經不能匹配了,至少有一個參數
re.exec('/')
//=> null
// 這裏卻是跟*同樣
re.exec('/bar/baz')
//=> ['/bar/baz', 'bar/baz']
複製代碼
全部的參數均可以提供自定義的匹配規則,來覆蓋默認規則([^/]+),以下匹配數字的例子:
// 這裏自定的規則就是咱們的數字匹配了(\d+)
var re = pathToRegexp('/icon-:foo(\\d+).png')
// keys = [{ name: 'foo', ... }]
re.exec('/icon-123.png')
//=> ['/icon-123.png', '123']
re.exec('/icon-abc.png')
//=> null
複製代碼
注意:自定義規則中反斜槓()前面須要再加一個反斜槓,例如上線的例子(\d+)(這裏跟正則不太一致,記得別混淆)
未命名的參數固然也是可行的,即只包含修飾符的羣組。和命名參數的功能同樣,只不過其name不是對應的key而是數字下標
// 第二個區塊,匹配的是全部字符.*,顯然是未命名的
var re = pathToRegexp('/:foo/(.*)')
keys = [
{ name: 'foo', ... },
// name就是0了,再有一個則按順序排列
{ name: 0, ... }]
// 結果沒什麼差異。
re.exec('/test/route')
//=> ['/test/route', 'test', 'route']
複製代碼
注意: react-router v4 再也不處理querystring了,你們可使用各類工具來處理,本身擼個工具也行。
到這裏參數部分已經結束了,回到上面的部分,/a/1/3.html。後面兩個參數可選。 具體規則能夠以下配置。
const routes = [
{ component: Root,
routes: [
{
path: '/a(/)?:num1?(/)?:num2?(/)?',
exact: true,
component: Home
}
]
}
]
複製代碼
是否是感受日了那什麼,有這麼複雜嗎,來咱們仔細看看有沒有這麼複雜。
看起來應該是這樣。那麼來試一試吧。
var re = pathToRegexp('/a/:num1?/:num2?.html')
// 第一種狀況是知足的,而且正確的獲得value了。 3,4
console.log(re.exec('/a/3/4.html'))
// [ '/a/3/4.html', '3', '4', index: 0, input: '/a/3/4.html' ]
// 這裏看起來沒問題,可是咱們第一個匹配num1 是 undefined
// 這樣順序就亂了,這裏應該是num1而非num2
console.log(re.exec('/a/4.html'))
// [ '/a/4.html', undefined, '4', index: 0, input: '/a/4.html' ]
// 直接不能匹配了
console.log(re.exec('/a.html'))
// null
複製代碼
這裏的問題就在於連續兩個可選參數的狀況下,單純的使用?就不知足了。
按照上面的表達式,匹配的應該是第一個參數可選,但只有一個參數時,4.html連着一塊兒,認爲是num2的value了。
上面的表達式轉換爲正則以後以下,有興趣能夠研究下: 這裏的4.html命中的是後面的([^/]+?)?.html(?:/)?$
/^\/a(?:\/([^\/]+?))?\/([^\/]+?)?\.html(?:\/)?$/i
複製代碼
對着上面的文檔思考下,能夠自定義可選參數,那麼咱們可不能夠這樣來試試(講真的,開始真是試的):
指明前綴也是可選,代表.html不是跟最後一個區塊緊密相連,這樣應該能夠知足要求
var re = pathToRegexp('/a(/)?:num1?(/)?:num2?.html')
console.log(re.exec('/a/0/4.html'))
//[ '/a/0/4.html', '/', '0', '/', '4', index: 0, input: '/a/0/4.html' ]
// 知足需求,這樣4其實爲num2的value
console.log(re.exec('/a/4.html'))
//[ '/a/4.html','/','4',undefined,undefined,index: 0,input: '/a/4.html' ]
// 第三種知足狀況
console.log(re.exec('/a.html'))
// [ '/a.html',undefined,undefined,undefined,undefined,index: 0,input: '/a.html' ]
複製代碼
這樣總算知足需求了。
有如下這麼幾個,這裏就不詳細介紹了。
到這裏關於react-router V4 路由規則部分的解析就結束了。原由也是本身在配置路由時有點懵,不想就那樣跟着別人的路由配完就完了。知其然也要知其因此然,應該是咱們技術人員一直秉承的一個態度,因此本身總結了一下,拋磚引玉,以供本身記憶和有須要的同窗參考。
更多個人博客請移步