在react中,通常都是应用单页面利用(SPA),即整个页面只有一个html,而后通过不同的url地址进行组件的匹配和切换。

咱们看到的url地址可能会有两种模式,一种是 localhost:3000/home,一种是 localhost:3000/#/home,两种地址的区别在于有无#,有#的是依据hash来进行匹配,即url中的锚点,实质上是通过location.hash来扭转href,hash后的内容是不会发送给服务器的,没有#是通过html5的history来进行跳转,两者跳转后都不会进行刷新。

以下路由是美团和网易云音乐的,能够看到他们的路由地址的别离是基于hash和history

具体来说,通过#来辨别路由的实现原理是监听hash的变动,这里监听的形式是应用hashchange 事件,当url地址产生了变动时,通过匹配以后url的地址来进行展现,代码如下所示

<body>    <div>       <a href="#/home">首页</a>       <a href="#/about">对于</a>    </div>    <div id="content"></div> <script>   window.addEventListener('hashchange',()=>{     const content = document.getElementById('content')     switch(location.hash){       case '#/home':         content.innerHTML = '首页'         break       case '#/about':         content.innerHTML = '对于'         break         default:           content.innerHTML = ''      }   })  </script></body>

通过html5的history来实现跳转无刷新,就须要阻止a标签的默认行为,再通过history的pushState这一办法实现url地址的替换,同样是监听url地址的变动,这里应用popState办法,当url地址产生了变动之后,展现对应的内容。

<body>  <div>    <div>      <a href="/home">首页</a>      <a href="/about">对于</a>    </div>    <div id="content">   </div></body><script>  const content = document.getElementById('content')  const aEles = document.getElementsByTagName('a')  for (let el of aEles) {    el.addEventListener('click', (event) => {      event.preventDefault()      const href = el.getAttribute("href")      history.pushState({}, '', href)      urlchange()    })  }  window.addEventListener('popstate', () => {    urlchange()  })  function urlchange() {    switch (location.pathname) {      case '/home':        content.innerHTML = '首页'        break      case '/about':       content.innerHTML = '对于'        break      default:        content.innerHTML = ''    }  }</script>

以上的锚点和history别离对应了react-router中的HashRouter和BrowserRouter,在react-router中,想要应用路由组件必须在最外层包裹一层HashRouter或者BrowserRouter,来抉择想要应用的路由类型,而后能力应用react-router提供的其它组件,react-route中用于web端的库为react-router-dom,以下所有的组件都是从react-router-dom中导出。

上面来说说react-router的罕用组件

1、<Link>和<NavLink>,这两个标签都是由a标签的封装,通过to属性指定跳转的链接地址,<NavLink>比<Link>多的是能够指定选中的款式和类名,格局如

<Link to="/about">对于</Link>

通过<Link>标签,点击之后就能够跳转到指定的地址,此时即便外层包裹的是HashRouter,也不须要本人加上#,react-router会帮咱们在url上加上#

2、当应用<Link>定义了跳转的url地址后,此时须要指定匹配跳转该url地址时须要显示的内容,此时应用的是<Route>,通过path属性指定url地址,component属性指定渲染的组件,格局如

 <Route path="/about" component={About} />

加上了之后, /about这个地址显示的就是About这个组件里的内容。<Route>进行的是含糊匹配,一个url门路可能能够匹配多个Route,如果须要严格匹配的话,能够减少一个属性 exact,适宜没有二级路由的时候开启。

3、在有很多的Route的状况下,即便在第一个Route匹配到适合的之后,依然会持续向下匹配,直到最初一个,所以在所有的<Route>外包裹一个<Switch>标签能够让它进行惟一的匹配,匹配到适合的之后就不持续匹配了。

4、当所有的<Route>都无奈匹配到url上的地址时,能够定义 <Redirect>组件间接重定向到一个页面,通过to来指定路由地址,这个组件要搁置到<Route>的最初面,因为它和<Link>不同,<Link>是点击了之后才会跳转对应的地址,而<Redirect>会间接执行并跳转

<Redirect to="/about"/>

5、通过路由来匹配的组件称为路由组件,<Route path="/about" component={About} />,这里的About就是路由组件,和其它的组件是不一样的,路由组件的props里有一些数据,其中包含三大属性,history、location和match,history能够自定义页面的跳转,location用来获取url地址相干的信息,match能够用作动静路由的匹配。

但个别的组件是没有这些props属性的,如果个别的组件也须要这样一些属性的话,能够通过一个高阶组件 withRouter。如

class myCom extends PureComponent { }export default withRouter(myCom)

再来说说路由传参

有时候,咱们心愿在链接上带一个id值或者两个页面之间跳转的时候传递一些参数,这时候有三种路由传参形式

1、params传参

<NavLink to="/detail/1">商品详情</NavLink><Route path="/detail/:id" component={Detail}/>// 如果是自行定义跳转的地址能够通过 this.props.history.push("/detail/1")

此时的id就是动静的,能够在id这个地位传递任意的数值或者字符串,而后通过props里的match对象中的params获取动静匹配的内容

2、search传参

<NavLink to="/detail?id=1">商品详情</NavLink><Route path="/detail" component={Detail}/>// 如果是自行定义跳转的地址能够通过 this.props.history.push("/detail?id=1")

此时相当于在url上增加一个问号进行拼接,须要把地址拼成一种键值对的模式,通过 props里的location对象中的search属性获取从问号开始的匹配内容,这一种路由的匹配形式须要自行解析字符串

3、state传参

<NavLink to={ pathname: "/detail", state: { id: 1} }>商品详情</NavLink><Route path="/detail" component={Detail}/>// 如果是自行定义跳转的地址能够通过 this.props.history.push("/detail", { id: 1})

这样的传递参数形式通过props里的location对象中的state属性来获取传递的值,这种形式的能够间接以对象的模式传递,并且可传递的数据更多,这些参数不会显示在url上

用一个小的组合案例展现以上内容

import React, { PureComponent } from 'react'import { NavLink, Route, Switch, withRouter, Redirect } from "react-router-dom"import Home from "./pages/Home"import About from "./pages/About"import Detail from "./pages/Detail"import Product from './pages/Product'class App extends PureComponent {  jumpToProduct(){    this.props.history.push('/product')  }  render() {    return (      <div>        <NavLink exact to="/">首页</NavLink>        <NavLink to="/about">对于</NavLink>        <NavLink to="/detail/1">详情</NavLink>        <button onClick={e=>this.jumpToProduct()}>商品</button>        <Switch>          <Route exact path="/" component={Home} />          <Route path="/about" component={About} />          <Route path="/detail/:id" component={Detail} />          <Route path="/product" component={Product}/>          <Redirect to="/"/>          </Switch>      </div>    );  }}export default withRouter(App)