orangeyyy
1/1/2018 - 9:01 AM

react-router

概述

react-router是用于实现React项目中路由功能的库,对于构建SPA React项目非常有必要,react-router4.x对react-router整体进行了重写,API也相对于3.x有了很大的改变,可以理解4.x和3.x是两个完全不同的工具,3.x主张集中配置route,4.x则将每一个路由都当做普通React component,因此route的配置会融入到整个项目的代码中,它的作者把这个称之为动态路由。这里重点介绍react-router4.x的使用。

安装

react-router将之前的库拆分成以下三个库:

  • react-router:react-router的核心代码;
  • react-router-dom:Dom绑定的react-router,依赖react-router;
  • reat-router-native:react native绑定的react-router,依赖react-router;

对于react项目,我们只需要安装 react-router-dom即可:

npm install react-router-dom --save

Router

react-router提供好几种router来满足不同的需求:

  • BrowserRouter:使用传统网站方式的路由,用path来指定不同路由,对于这种Router,需要在服务端进行设置将所有路由范围内的请求指向到一个静态Html;
  • HashRouter:使用hash值来指定不同路由,对于使用static形式来作为服务端的情况,用这种router非常方便,不需要服务端做任何额外的配置;
  • MemeryRouter:将路由指定存放在内存中,通常用于测试环境、没有浏览器的环境或者是react native;
  • StaticRouter:用于不需要改变location的router;

其中使用最多的是BrowserRouter和HashRouter,这里用HashRouter举例:

import {HashRouter} from 'react-router-dom';

ReactDOM.render((
  <HashRouter>
    <App />
  </HashRouter>
), document.getElementById('root'))

Route

Route组件是react-router中最重要也是最常用的组件,它通常的作用是当url和组件的path属性匹配时渲染相应的UI组件。

Route可以通过3种方式来指定UI:

  • <Route component>
import {Route} from 'react-router-dom';
import Page1 from './page1';

export default (props) => {
  return (
    <div>
      <Route path="/page1" component={Page1} />
    </div>
  );
}

这种方式其实是route通过React.createElement来创建一个component指定组件的实例,需要注意的是如果component组件传入一个inline函数来返回一个组件,那么每次render就会创建一个新的实例,这样就导致已有的组件实例没有销毁,新的实例又创建了,而不是直接更新已有的实例,因此对于这种情况,建议使用下面两种方式。

  • <Route render>
import {Route} from 'react-router-dom';

export default (props) => {
  return (
    <div>
      <Route path="/page2" render={(props) => (
          <div>
            page2 yoyoyo
          </div>
        )} />
    </div>
  );
}
  • <Route children> 通过children属性来指定UI组件,接受一个参数为match的函数,使用者可以根据是否匹配path来选择渲染不同的UI组件。
import {Route} from 'react-router-dom';

export default (props) => {
  return (
    <div>
      <Route path="/page2" children={({match}) => {
        if (match) {
          return (<div> match the path</div>);
        } else {
          return (<div> not match the path</div>);
        }
      }} />
    </div>
  );
}

Route组件可以配置如下参数来指定url匹配规则:

  • path:string类型,path-to-regexp形式,用于指定需要匹配的path,若不指定,表明匹配所有的path;
  • exact:bool类型,用于指定完全匹配path,如下所示:
pathlocation.pathnameexactmatch?
/one/one/twofalseyes
/one/one/twotrueno
  • strict: bool类型,若设置为true且path末尾跟一个‘/’,那么只能匹配后面跟有‘/’的pathname,若设置为true,如下所示:

|path|location.pathname|match?| |-|-|-|-| |/one/|/one|no| |/one/|/one/|yes| |/one/|/one/two|yes|

  • location: object类型,默认情况下Route会匹配当前浏览器中的location,通过location我们可以自定义一个location来用于匹配;

Route在指定渲染UI组件时会给组件注入3个props:

  • match:是一个用描述match情况的对象,主要包含以下内容:

    • params:key/value对,用于记录path中变量的值,例如/page/:id中的id;
    • isExtract:bool类型,表示是否绝对匹配;
    • path:string类型,进行匹配的path,通常用于嵌套route的时候;
    • url:string类型,匹配的URL部分,通常用于嵌套link的时候;
  • location:用于记录当前location相关描述,通常是下面这个样子:

{
  key: 'ac3df4', // not with HashHistory!
  pathname: '/somewhere'
  search: '?some=search-string',
  hash: '#howdy',
  state: {
    [userDefined]: true
  }
}

在history的属性中也有一个history.location,但是通常建议使用这个,因为history下面的location会发生突变,这个不会。

  • history:history用于记录history相关的描述,主要包含以下属性:
    • length:history栈中的实体数量;
    • action:指明当前的action(PUSH、POP或REPLACE);
    • location:同上面介绍的location;
    • push:push一个新的实体到history栈中;
    • replace:替换history栈中的当前实体;
    • go:移动history栈中当前实体的指针;
    • goBack:相当于go(-1);
    • goForward: 相当于go(1);
    • block: 阻止导航;

Redirect

用于将当前locaction重定向到一个新的location,新的location将替换掉history栈中的当前实体。通常用法:

//接受string
<Redirect push to="/somewhere/else"/>
//接受object
<Redirect to={{
  pathname: '/login',
  search: '?utm=your+face',
  state: { referrer: currentLocation }
}}/>
//在switch中使用
<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>

Switch

Switch用来包裹Route和Redirect,并render第一个匹配的Route或Redirect。因此它是有排他性的,不使用switch的情况下,会渲染所有匹配的Route或Redirect。

如下所示,如果访问/about,三个组件都会被渲染:

<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>

如果按照下面的方式来写,往往更能达到用户的期望:

<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>

Switch中只能使用Route和Redirect子元素

Switch可以设置一个location熟悉来替代当前浏览器location

没有path的Route和没有from的Redirect都可以匹配所有链接

Link & NavLink

Link类似a标签,用于进行页面跳转,默认是使用push的方式,通常用法如下所示:

<Link to="/courses"/>

<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: { fromDashboard: true }
}}/>

<Link to="/courses" replace />

NavLink是一种特殊的Link,它可以将样式属性传递给当前组件,如果to设置的path匹配当前URL,最常见的场景就是导航条。基本用法如下:

//设置class
<NavLink
  to="/faq"
  activeClassName="selected"
>FAQs</NavLink>

//设置属性
<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}
>FAQs</NavLink>

//支持exact和strict属性
<NavLink
  exact
  strict
  to="/profile/"
>Profile</NavLink>

我们也可以设置一个isActive属性来处理一些特殊逻辑:

const oddEvent = (match, location) => {
  if (!match) {
    return false
  }
  const eventID = parseInt(match.params.eventID)
  return !isNaN(eventID) && eventID % 2 === 1
}

<NavLink
  to="/events/123"
  isActive={oddEvent}
>Event 123</NavLink>

参考