前言

react-router升级到4之后,跟前面版本比有了很大的差别。

例如包的拆分,动态路由等详细的差别就不说了,各位大神的总结也很到位,详细可以点击看看,All About React Router 4这篇文章

此外还有个差别是路由规则的变化。 一直有着上个版本的习惯,所以稍微复杂的路由,配起来的时候简直痛不欲生。

痛定思痛,要好好了解下其依赖的匹配规则,即path-to-regexp

本文期望读者是对react-router有过使用的同学,不然本文省略了太多东西,可能看起来可能有点太乱。

path-to-regexp 是什么

其文档一句话介绍很简洁明了: 将路径字符串(如/user/:name)转换为正则表达式。react-router matchPath就是基于其来匹配了。

使用

var pathToRegexp = require('path-to-regexp')

// pathToRegexp(path, keys?, options?)
// pathToRegexp.parse(path)
// pathToRegexp.compile(path)

参数:

  • path: 字符串、字符串数组、正则表达式
  • keys 可选 由在path里找到的key组成的数组
  • options 可选 由下面几部分组成:
    1. 敏感匹配 默认false,当为true时,正则将区分大小写
    2. 严格模式 默认false 为true,将会匹配可选的紧跟的分隔符
    3. end 默认true 正则是否匹配至字符串结尾
    4. start 默认true 是否从字符串开始进行匹配
    5. 高阶选项(用于非路径名称字符串,例如主机名称hostname)
      1. 分隔符 默认每段的分隔符是'/'
      2. 结尾字符 可选字段或字段列表、用于作为结束字符
      3. 分隔符列表 解析时要当做分隔符考虑的字段列表 默认‘./’

还是直接看官方例子吧

// 匹配的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都是可以省略的也是可选的,也就是说如下面这样:

'/a/1/3.html'
'/a.html'
'/a/2.html'

先不要急着写,这种当然是要有按照相应规则来匹配了,先看下对应规则:

参数

路径参数将会被用来定义参数和匹配关键字列表(即我们的keys)

命名参数

命名参数通过如下形式定义: 在参数前面加上引号,例如:‘: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

当然参数可以以*结尾,标识该部分参数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-n

参数以+结尾时,表明该部分参数至少为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
}
]
}
]

是不是感觉日了那什么,有这么复杂吗,来我们仔细看看有没有这么复杂。

  1. /a是固定的,可以不变,第一部分确定。
  2. 后面这个1对应num1,且可选 /a/:num1?
  3. 3对应num2,同样可选 /a/:num1?/:num2?.html

看起来应该是这样。那么来试一试吧。

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(?

react-router v4 路由规则解析的更多相关文章

  1. 从 React Router 谈谈路由的那些事

    React Router 是专为 React 设计的路由解决方案,在使用 React 来开发 SPA (单页应用)项目时,都会需要路由功能,而 React Router 应该是目前使用率最高的. Re ...

  2. [Web 前端] React Router v4 入坑指南

    cp from : https://www.jianshu.com/p/6a45e2dfc9d9 万恶的根源 距离React Router v4 正式发布也已经过去三个月了,这周把一个React的架子 ...

  3. React Router V4发布

    React Router V4 正式版发布,该版本相较于前面三个版本有根本性变化,遵循 Just Component 的 API 设计理念. 本次升级的主要变更有: 声明式 Declarative 可 ...

  4. React Router v4 页面传值的三种方法

    传值方法 1.props.params 使用React router定义路由时,我们可以给指定一个path,然后指定通配符可以携带参数到指定的path: <Route path='/user/: ...

  5. [React Router v4] Intercept Route Changes

    If a user has entered some input, or the current Route is in a “dirty” state and we want to confirm ...

  6. [React Router v4] Redirect to Another Page

    Overriding a browser's current location without breaking the back button or causing an infinite redi ...

  7. [React Router v4] Render Multiple Components for the Same Route

    React Router v4 allows us to render Routes as components wherever we like in our components. This ca ...

  8. [React Router v4] Conditionally Render a Route with the Switch Component

    We often want to render a Route conditionally within our application. In React Router v4, the Route ...

  9. [React Router v4] Render Catch-All Routes with the Switch Component

    There are many cases where we will need a catch-all route in our web applications. This can include ...

随机推荐

  1. 2018-2019-2 网络对抗技术 20165227 Exp5 MSF基础应用

    2018-2019-2 网络对抗技术 20165227 Exp5 MSF基础应用 Exploit选取 主动攻击:ms17_010_eternalblue(成功) 浏览器攻击: ms10_042_hel ...

  2. 20165227《网络对抗技术》Exp0 Kali安装 Week1

    2018-2019-2 <网络对抗技术>Exp0 Kali安装 Week1 kali下载:镜像文件通过同学获得 kali具体安装步骤: 打开VMware,点击新建虚拟机,进行创建 创建完成 ...

  3. Python3学习笔记21-实例属性和类属性

    由于Python是动态语言,根据类创建的实例可以任意绑定属性. 给实例绑定属性的方法是通过实例变量,或者通过self变量: class Student(object): def __init__(se ...

  4. crontab定时作业

    crontab用于在指定时间执行某项作业,如执行脚本或命令. 1.crontab -e      创建一个crontab文件,并添加作业(这个crontab的拥有者是创建者). 这里要注意:必须指定s ...

  5. 017_mac格式化硬盘,mac如何格式化硬盘

    想做一个mac和windows都能识别的系统,推荐设置成什么格式 一.在mac下格式化 在Mac 下,打开右下角应用程序-实用工具-磁盘工具,里面选取你的移动硬盘,然后进行格式化,设置成EXFat格式 ...

  6. 路由器中继(repeater)模式 和 AP+WDS模式区别?

    理论上的 中继(repeater)模式, 只有连接的最后一个才会有信号发出,中间的连接节点是没有信号发出的. AP+WDS模式:就是每一个路由都有信号发出,可以进行信号的全方位覆盖.

  7. tomcat8配置SSL

    参考网址:http://www.micmiu.com/enterprise-app/sso/sso-cas-sample/#viewSource 1.生成证书 keytool -genkey -ali ...

  8. Android studio下将项目代码上传至github包括更新,同步,创建依赖

    AS中设置GIT 一.开篇 本文讲如何使用Android Studio将项目上传到github,虽然讲上传github的文章很多,但是大部分都是使用Git Bash命令行,虽然效率高些,但是有点麻烦, ...

  9. OCM_第十六天课程:Section7 —》GI 及 ASM 安装配置 _安装 GRID 软件/创建和管理 ASM 磁盘组/创建和管理 ASM 实例

    注:本文为原著(其内容来自 腾科教育培训课堂).阅读本文注意事项如下: 1:所有文章的转载请标注本文出处. 2:本文非本人不得用于商业用途.违者将承当相应法律责任. 3:该系列文章目录列表: 一:&l ...

  10. python接口自动化测试八:更新Cookies、session保持会话

    s = requests.session() # 此方法只适用于网站是cookies这种,网站是token的没用 # 这样做的好处就是可以保存cookies并保持会话,不用每次都去获取.传参 Toke ...