1 为什么需要使用react-router
当你写了很多react组件后,想要实现不同组件之间的切换和跳转的时候,react-router就派上用场了。
2 使用react-router-dom还是react-router
两者区别不大,一般只使用react-router-dom就能满足我们的需要。
3 安装方法
npm install --save react-router-dom
4 如何使用
使用前要先引入
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'//这里将BrowserRouter重命名为Router
使用举例
先打开本地服务器,如果不懂怎么做,请看我的另一篇文章http://www.jianshu.com/p/d08db717a66f
<Router>
<div>
<ul>
<li><Link to="/">主页</Link></li>
<li><Link to="/hot">热门</Link></li>
<li><Link to="/zhuanlan">专栏</Link></li>
</ul>
<hr/>
<Route exact path="/" component={App}></Route>
<Route path="/hot" component={Hot} ></Route>
<Route path="/zhuanlan" component={Zhuanlan}></Route>
</div>
</Router>
<Router></Router>相当于一个盒子,里面只能包含一个子元素。
react-router4之后,<Router></Router>标签里面可以包含任何html标签和react组件。
然而并不是react-route的其他标签必须包在Router里面,比如只要通过import { Route, Link} from 'react-route-dom'引入,可以随意单独使用Route和Link标签。
<Link></Link>标签和<a></a>标签类似,点击<Link></Link>标签中的元素可以实现组件跳转。跳转到哪个组件要看path的值"/"代表根路径,相当于localhost:8080这个路径,我们发现有一个<Route></Route>的path属性和这个Link标签的path值相同,当用户点击Link标签时,会触发和它具有相同path值的Route的component渲染,并且浏览器的网址那里的url变成他们的path值代表的路径。
因此,我们可以理解,“路由”就是要实现url跳转,注意不要和直接打开html文件哪个本地文件路径搞混了,这里改变的是网址URL,就是你的path值,前面加上localhost:8080。
那么问题来了,url和文件路径的区别是什么?
我们以前练习的时候,打开我们的index.html是手动直接打开的,它在浏览器中显示的路径就是以我们的电脑哪个磁盘开始的资源路径,就是这个文件在电脑中的位置。
url路径不同于资源路径,我们的组件Hot的url路径是localhost:8080/hot,这是我们自己定义的,和Hot组件在电脑中的位置无关,反正只要我们在<Route></Route>中定义了这个component的path,那么这个组件的url就确定了,不用管它在电脑中的位置,当然,在当前文件中必须先引入这个组件,还是按电脑中的相对路径来的。所以,你理解资源路径和url的区别了吗?
5 history的理解
如果你用过老版本的react-router,你一定知道history。history是用来兼容不同浏览器或者环境下的历史记录管理的,当我跳转或者点击浏览器的后退按钮时,history就必须记录这些变化。
browserHistory h5的history兼容性不好,需要后端支持,在渲染组件的时候转成hashHistory。
<Router history={browserHistory} routes={routes} />,
这种情况需要对服务器改造。否则用户直接向服务器请求某个子路由,会显示网页找不到的404错误。
browserHistory 模式下,URL 是指向真实 URL 的资源路径,当通过真实 URL 访问网站的时候(首页),这个时候可以正常加载我们的网站资源,而用户在非首页下手动刷新网页时,由于路径是指向服务器的真实路径,但该路径下并没有相关资源(因为url路径是path中设置的路径,并不是资源路径),用户访问的资源不存在,返回给用户的是 404 错误
官方推荐使用browserHistory
History API 提供了 pushState() 和 replaceState() 方法来增加或替换历史记录。
而 hash 没有相应的方法,所以并没有替换历史记录的功能。但 react-router 通过 polyfill 实现了此功能,具体实现没有看,好像是使用 sessionStorage。
另一个原因是 hash 部分并不会被浏览器发送到服务端,也就是说不管是请求
http://domain.com/index.html#foo 还是http://domain.com/index.html#bar ,服务只知道请求了 index.html 并不知道 hash 部分的细节。而 History API 需要服务端支持,这样服务端能获取请求细节。
还有一个原因是因为有些应用会忽略 URL 中的 hash 部分。
对于初学者,我推荐先使用<Router history={hashHistory}>,并且在引入Router时使用HashHistory as Router代替BrowserHistory as Router,以免出现不必要的麻烦。
6 react-router4路由嵌套
什么是路由嵌套
路由嵌套,顾名思义就是路由/URL路径的嵌套,比如,若Hot组件的路由是localhost:8080/hot,如果我们想要得Hot组件添加一个子组件Girl,每次我们渲染Girl的时候,Hot也会自动和他一起渲染,那么可以这样写
//父组件中
<Router>
<div>
<Route exact path="/" component={App}></Route>
<Route path="/hot" component={Hot} ></Route>
<Route path="/zhuanlan" component={Zhuanlan}></Route>
</div>
</Router>
//子组件中
<Route path="/hot/girl" component={Girl} ></Route>
理解<Route></Route>
在react-router4中,将他看做一个类似于组件的东西,你想在哪里渲染组件,就把他写在哪里
//入口组件app.js
<Router history={hashHistory}>
<div>
<Route exact path="/" component={PCIndex}></Route>
<Route path="/hf" component={HeaderAndFooter}></Route>
</div>
</Router>
//HeaderAndFooter组件
<div>
<PCHeader />
<Route path="/hf/paper" component={BodyPaper}></Route>
<Route path="/hf/people" component={BodyPeople}></Route>
<BodyFooter />
</div>
路由对应:
localhost:8080----渲染PCIndex组件
localhost:8080/hf----渲染HeaderAndFooter组件
localhost:8080/hf/paper----同时渲染BodyPaper和HeaderAndFooter组件组件,并且BodyPaper组件在PCHeader组件和BodyFooter组件的中间
localhost:8080/hf/people----同时渲染BodyPeople和HeaderAndFooter组件组件,并且BodyPeople组件在PCHeader组件和BodyFooter组件的中间
7 组件路由参数传递match和:id的理解
match参数的理解
通过Route路由的组件,可以拿到一个match参数,这个参数是一个对象,其中包含几个数据:
isExact:这个关键字表示是为作全等匹配,比如,写了exact,“/”只能匹配PCIndex组件,而不写那就问题大了,在localhost:8080路径下,还可以匹配到HeaderAndFooter组件,并将其渲染。
params:path中包含的一些额外数据
path:Route组件path属性的值
url:实际url的hash值
示例
const HeaderAndFooter = ({match}) => (//{match}要先引入
<div>
<PCHeader />
<Route path={`${match.url}/paper`} component={BodyPaper}></Route>
<Route path="/hf/people" component={BodyPeople}></Route>
<BodyFooter />
</div>
)
export default HeaderAndFooter;
${match.url}获取到的路径就是HeaderAndFooter的path路径
这里用到了es6的模板字符串功能,两个反单引号括起来的字符串,里面可以放js语句,比如变量,但需要将变量名写在${}之中
模板字符串之中还能调用函数let msg = Hello, ${place}
;
:id的理解
:id其实是官网文档中的一种写法,你也可以任意写这个参数,它放在url中,也是用来传递参数的。它不是要参与url路由,react-router会通过props.params传递给组件,id表示参数变量,id有实际值将替换掉 :id ,变为实际参数。
比如:
当我们请求/home/123/app/456的时候, 路由就会匹配到/home/:userId/app/:appId
其中params就是指的上面的userId 和 appId
转换成json就是{userId: 123, appId: 456}
利用这种参数传值:
示例
<Route path="/hf/people" component={BodyPeople}></Route>
<Route path={`${match.url}/:id`} component={BodyPaper}></Route>
当我们访问localhost:8080/fh/people的时候,就会渲染BodyPeople组件,people也会替换了url中的:id,渲染BodyPaper组件。
可以用Switch解决这个问题,Switch下每次只匹配一个路由,并且将这种含参数的url往下写也是一个很好的习惯。
<Switch>
{/* 用了Switch 这里每次只匹配一个路由,所有只有一个节点。 */}
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
</Switch>
综合示例
//父组件中
const Hot=({match})=>(<div>
<h2>热门</h2>
<Link to={`${match.url}/article`}>文章</Link>
<Link to={`${match.url}/qa`}>问答</Link>
<Link to={`${match.url}/news`}>新闻</Link>
<hr/>
<Route path={`${match.url}/:type`} component={Content}></Route>
</div>)
//子组件中
const Content=({match})=>(
<div>
<h2>热门子目录</h2>
<p>{match.params.type}</p>
</div>
)
可以发现match.params.type轻松取到了url中的参数。