2019年不知不觉已通过去19天了,有没有给本身作个总结?有没有给明年作个计划?
固然笔者已经作好了明年的工做、学习计划;同时也包括该系列博客剩下的博文计划,目前还剩4篇:分别是两篇React-Router4
和两篇immutability-helper
。本篇是React-Router4的第一篇,在正式开始以前你们能够先看下实际效果,这是笔者用React-Router4写的例子。react
实际上,笔者接触的第一个React中的路由模块就是React-Router,只不过当时仍是v3版本。由于没有太多深刻得学习研究,因此在这里不会对v4以前的版本做介绍或者评价(其实并不表明笔者对v4版本有深刻的研究学习,汗...)。下面列举些React-Router4中须要知道的一些概念或者emmmm...小知识点。git
React-Router4中的包主要有三个react-router、react-router-dom和react-router-native。据考证(手动斜眼笑),React-Router4已经将前身切分了出来分别整合成了单独的module。其实笔者一开始看到也挺懵逼的,这三个包究竟是什么玩意?github
react-router
:“The core of React Router”。简单说就是逻辑代码。
react-router-dom
:“DOM bindings for React Router”。这个模块不只仅包含了react-router的模块,还包含了页面渲染功能,也就是能够在页面上显示。
react-router-native
:“React Native bindings for React Router”。这个也很好理解,就是能够在React-Native中使用。
React-Router4的理念是一切皆组件
。React-router-dom
则提供了两个路由根节点
:HashRouter和BrowserRouter。express
HashRouter
: 经过hash值来对路由进行控制,并且你会发现一个现象就是url中会有个#
,例如localhost:3000/#。对于笔者这种有强迫症的人来讲怎么能忍?因此笔者就没再碰这个组件。
BrowserRouter
: BrowserRouter就相对舒服点。它的原理是使用HTML5 history API (pushState, replaceState, popState)来使你的内容随着url动态改变的,而不会出现莫名其妙的#
。
上面提到:React-Router4的理念是一切皆组件
(如下统一称“组件”
)。咱们v4以前的版本都须要在一个js文件中配置整个项目的路由信息而后在App.js
(以create-react-app脚手架建立的React项目为例)引入并使用。而在React-Router4中则将全部的功能以React组件的形式提供出来,意思就是说咱们能够像使用普通组件同样使用路由组件并最终在项目中造成一棵路由树
。在这里笔者要注重说下,一个项目中尽可能只有
一棵路由树,除非有特殊需求,不然的话会形成一些奇怪的问题。通俗点说就是不要在一棵树上栽另外一棵树。下面用一张图来简单展现下React-Router4的使用:react-router
如图是React-Router4使用规则和最简单的使用实践。
固然这种写法(我称它叫组件型路由
,下同)和以前的配置型路由
哪一个更好用是青菜萝卜的问题,不须要太多纠结,本身喜欢就好。接下来的介绍都是笔者经过学习官方文档并作了一些实践后的心得。若有不当或者错误,欢迎指正。对于第一次学习该模块的童鞋,笔者建议将代码下载下来一遍看代码一遍看本文,也能够打开笔者的demo,这样会更深入。app
下面这段代码是该demo根路由配置的代码(其实不存在什么根路由概念,只是笔者喜欢这么叫它)。从这里就能明显看出来react-router3和4两个版本的差别。react-router3有本身独立的一个配置中心文件,而react-router4则把跳转的规则嵌入到实际代码中,不须要额外维护一个路由配置文件。从这点来讲的确方便了很多,也迎合React一切皆组件的理念。dom
<Router> <div className={AppStyle["Container-body"]}> <nav className={AppStyle["App-nav-list"]}> <ul> <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/basic">基础路由</NavLink></li> <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/param">带参路由</NavLink></li> <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/nesting">嵌套路由</NavLink></li> <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/ambiguous">暧昧匹配</NavLink></li> <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/auth">权限路由</NavLink></li> <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to="/404">404</NavLink></li> </ul> </nav> <div className={AppStyle.content}> <Switch> <Redirect exact from="/" to="/basic" /> <Route path="/basic" component={renderBasicRouter} /> <Route path="/param" component={RouterWithParameters} /> <Route path="/ambiguous" component={renderAmbiguousRouter} /> <Route path="/nesting" component={renderNestingRouter} /> <Route path="/auth" component={authenticationRouter} /> <Route component={NoMatch} /> </Switch> </div> </div> </Router>
从这段代码中须要了解的概念包括:Router
、NavLink
、Route
、Redirect
、exact
、Switch
。学习
上面已经介绍过了,这里笔者用的是BrowserRouter
测试
NavLink能够触发路由的跳转,固然相似的组件还有Link。它们均可以经过指定属性to
的值来告诉Router咱们要跳转到那个Route,实际上NavLink(Link)
和Route
本就已经经过to
和path
两个属性创建起关系了。而NavLink与Link的区别在于各自API的数量,由于NavLink可用的API相对较多,因此笔者更青睐NavLink。具体API有兴趣的能够自行Google,经常使用的API笔者已经在项目中使用。网站
Route组件是React Router4中主要的组成单位,能够认为是NavLink或Link的路由入口
。在这里笔者要重点Tip
一下,上一条说NavLink或Link能够触发路由的跳转,实际上它们的实现流程是这样的:
NavLink(Link)改变地址栏的pathname
,Router会根据pathname去匹配它子组件中的Route中的path
属性,一旦匹配上就会渲染该Route对应的组件`。因此NavLink(Link)与Route并无并存的关系,由于NavLink(Link)只是用来改变pathname,不会直接去调用任何API。因此当咱们在地址栏直接手动输入路由,也会发生路由渲染。
具体匹配规则请参考path-to-regexp
,或者经过这个网站进行测试。
Redirect至关于转发器。Router内部去匹配路由入口
的时候也会去匹配Redirect的from
属性值。一旦匹配到了Redirect,Redirect就会将这个跳转请求转向本身的to
属性值对应的路由。因此这个过程能够这样理解:当咱们访问页面路径的时候,好比:http://ip:3001/
,就会捕获到'/'
这个路由跳转请求,Router开始在Route中匹配随后匹配到了Redirect,Redirect自行发起路由跳转请求'/basic',Router开始像往常同样在Route中匹配直到匹配到了path为"/basic"的Route,随后对Route对应的component进行渲染。
exact将该Route标示为严格匹配路由
。什么叫严格匹配路由?就是pathname必须与Route的path属性值彻底一致才算匹配上。例如:
pathname | path | 匹配结果 |
---|---|---|
/home | /home | 能够匹配 |
/home/child | /home | 没法匹配 |
这里Tip下:
若是某个Route对应的组件中也有Route,那么千万不要
在这个Route中加 exact,否则你会发现彻底匹配不到子路由。切记,由于笔者最近刚踩过这个坑。因此这里笔者建议你们只在叶子Route
中使用exact,也就是最后一级Route中使用exact,固然exact '/' 除外
。
Switch能够将多个Route包裹成一组Route,当进行路由匹配时候,该组中的路由一旦发生匹配那么就不会匹配改组中剩下的路由
。也就是说该组中的路由最多只会被匹配到一个
。
追加一条。
Route的component属性对应的属性值一般是一个组件或者一个方法。而若是是方法的话,好比例子中:
const renderBasicRouter = ({ match }) => <Basic url={match.url} path={match.path} />;
那么传入的参数为:
因此若是在跳转前有什么特殊逻辑须要处理,好比咱们想让不一样的页面有不一样的前缀,好比例子中的/basic/Home
、/param/home
等,那么就能够如例子同样处理。
但若是不须要特殊处理的话,直接把组件放到component属性下便可。固然router相关的参数也会经过props传给该组件。
这里Tip下:
一、Route中还有一个render属性,也能够用来渲染组件,可是当咱们渲染被Redux处理过的组件时候可能会有问题,由于Redux会在原组件基础上多包裹一层。
二、若是
固然经常使用路由组件还不只仅这些,后续例子会有补充。
基础路由的使用比较简单,前面的介绍其实已经把它基础使用方法已经说了。不过笔者这里有个小tip:
match.url
经常使用于NavLink或者Link中拼接路由路径,好比:
<NavLink to={`${URL}/Home`}></NavLink>
match.path
经常使用于Route中拼接路由入口路径,好比:
<Route path={`${PATH}/Home`} component={HomePage} />
这里Tip下:
当URL中不带有任何参数的时候,match.path和match.url彻底一致。若是带有参数的话可能会有点编码上的差别。
带参路由在实际开发中用的比较多,这里只介绍location.pathname中的参数,location.search笔者没有研究过因此就不说了,省得误导你们。咱们能够先看下demo例子的部分代码:
<nav className={Style["Params-nav-list"]}> <ul> <li><NavLink exact activeStyle={{ fontWeight: 'bold', color: 'red' }} to={`${URL}/name`}>/name</NavLink></li> <li><NavLink exact activeStyle={{ fontWeight: 'bold', color: 'red' }} to={`${URL}/name/Mario`}>/name/Mario</NavLink></li> <li><NavLink activeStyle={{ fontWeight: 'bold', color: 'red' }} to={`${URL}/check/true`}>/check/true</NavLink></li> </ul> </nav> <div className={Style.content}> <Switch> <Route exact path={`${PATH}/name/:name`} component={Name} /> <Route exact path={`${PATH}/name`} component={Name1} /> <Route exact path={`${PATH}/check/:check(true|false)`} component={Check} /> <Route component={NoMatch} /> </Switch> </div>
这里能够看到Route的配置有点不一样,用过express的朋友都知道,路由中经过 :xxx
来标示这是一个参数。其实这里也同样。如例子所示咱们在 path={${PATH}/name/:name
}经过 :name
来标示这是一个参数。而后对应的导航也有 to={${URL}/name/Mario
}。因此这个流程就至关于:导航到/name路由而且传 name为Mario的参数
。这个参数能够在对应组件的props中拿到(this.props.match.prams.name
)。咱们还看到 path={${PATH}/check/:check(true|false)
},在参数后有个括号而且里面还有管道符,其实这是限定check的参数值必须为true或者false这两个
。细心的朋友可能注意到,我在每一个Route中加了exact,这样作的好处是能够不用考虑Route的放置次序。举个例子解释下:若是咱们想查看某我的的信息,那么跳转路由应该是 /user/4
,但若是Route中有:
<Switch> <Route exact path={/user} component={User} /> <Route exact path={/user/:id} component={User} /> </Switch>
那么/user/4
就会在匹配到/user
后停下渲染User组件而且忽略了参数。有人说把Switch去掉不就好了吗?并非,那么/user/4
会同时匹配上这两个路由而且什么都不会渲染,由于它懵逼了,不知道渲染哪一个。因此这种状况下须要调整它们的位置
<Switch> <Route exact path={/user/:id} component={User} /> <Route exact path={/user} component={User} /> </Switch>
这样就不会出现上述问题了。可是若是在每个Route中使用exact(前提是这个Route是叶子Route),就不用考虑Route的次序问题了。
有时候会因为各类问题出现匹配不到任何Route的状况,这个时候为了更好的用户体验,咱们会配置一个404路由,形如:
<div className={Style.content}> <Switch> <Route exact path={`${PATH}/name/:name`} component={Name} /> <Route exact path={`${PATH}/name`} component={Name1} /> <Route exact path={`${PATH}/check/:check(true|false)`} component={Check} /> <Route component={NoMatch} /> </Switch> </div>
不过笔者发如今根路由中配置一个404后没法全局抓取路由404,不知道是本就如此仍是用法有误,因此笔者在每一级路由中都配置了 <Route component={NoMatch} />
。写法很简单,照着写就行了,component中传入显示404信息的组件便可。
顺便加一下demo源码