美文网首页
react路由

react路由

作者: 指尖轻敲 | 来源:发表于2018-08-01 15:59 被阅读53次

    react-router-dom


    最近开始学习reactr-router,但是看了好多文章都有问题,后来才发现是react-router 4做了很大的改动,遵循 Just Component 的 API 设计理念。reactr-router被拆分为reactr-routerreactr-router-domreactr-router-native三部分。reactr-router提供核心的API,其余两个则提供两个特定的运行环境(浏览器和react-native)所需的组件。因此所有的路由组件导入都改为了从react-router-dom导入。

    安装

    注意如果是用create-react-app脚手架的话,注意安装方式(yarn或者npm),详情参考另外一篇react脚手架搭建

    npm install --save react-router-dom;
    npm install --save react-router-redux;
    npm install --save react-router-config;
    

    使用

    先看一下基本写法:

    一级路由,做正常的路由切换
    import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom'
    class App extends Component {
      render() {
        return (
          <Router basename='/route'>
            <div>
              <Route path="/" exact component={Home}></Route>
              <Route path="/mime" component={Mime}></Route>
              <Route path="/list" component={List}></Route>
              <ul>
                <li>
                    <Link to="/">home</Link>
                </li>
                <li>
                    <Link to="/mime">mime</Link>
                </li>
                <li>
                    <Link to="/list">list</Link>
                </li>
              </ul>
            </div>
          </Router>
        );
      }
    }
    
    二级路由

    在一级路由的基础上再做路由,通过props中的match属性可以获取当前的url(),然后再去拼接二级路由的path。

    export default class List extends React.Component {
      constructor(props) {
        super(props);
      }
      render() {
        return (
          <div>
            <h1>列表页</h1>
            <ul>
              <li>
                <Link to={`${this.props.match.url}/details`}>详情</Link>
              </li>
              <li>
                <Link to={`${this.props.match.url}/edit`}>编辑</Link>
              </li>
            </ul>
            <Route path={`${this.props.match.url}/details`} component={Details} />
            <Route path={`${this.props.match.url}/edit`} component={Edit} />
          </div>
        );
      }
    }
    
    带参数

    如果路由后面要携带参数,可以这样写(最后括号中的值表示只能是这两个值,如果没有此限制则可以是任何值)

    <Route
        path={`${this.props.match.url}/:id(20|100)`}
        component={Details}
    />
    

    在子组件中通过this.props.match.params.xx获取对应的参数

    <h1>{this.props.match.params.id}</h1>
    
    <BrowserRouter>和<HashRouter>

    他们就是路由的根基,包裹在最外层。之前使用路由的肯定知道(不管是哪个版本,甚至vue的路由),路由的实现有两种方法,HashRouter是利用URL的hash部分实现,在url中会经常有个#号,主要是针对旧版浏览器。我们一般都使用BrowserRouter,它是利用H5的history API实现。

    还有一种实现是针对非DOM环境的(react-native),利用内存实现。

    • basename:如果你的文件在服务器的二级目录下,就会用到它。他会在每一个路由路径前面自动添加一级目录。如下图,当我们点击list时,url中会自动添加route目录。
    basename.png

    该组件下只能有一个子组件,所有组件都要包裹在一层里。

    <Route>

    Route就是控制不同路径去对应显示内容的组件。配合Link使用。

    • exact:控制严格匹配。如果没有这个属性,当访问list时,home组件和list组件会同时被渲染。

    • path:访问的路径,如果没有该属性,那么将会匹配一切路径。

    • component:path路径对应要显示的组件。使用最多。如果要获取match等对象。可以使用this.props.match...

    • render:与component一样,可以直接在后面写要渲染的内容。可以获取到mach对象。

    <Route path="/home" render={({match}) => <div>Home</div>}/>
    
    • children:和render是一样的功能,不过它不论是否匹配到都会调用。
    <Route path="/mime" component={Mime}></Route>
    <Route path="/list" children={({match})=> (
        <div>
            <h3>哈哈</h3>
            <List></List>
        </div>
    )}></Route>
    

    以上代码,不管当前匹配的是mime还是list或是其他,都会渲染list。

    <Link>和<NavLink>

    Link的api相对较少,上面我们使用的是最简单的to,直接跳转到一个地址。就不多说了。

    • to:还可以携带参数到指定页面,后面跟一个对象就可以。如下(可以实现页面间传参)
    <Link to={{
      pathname: '/list',
      search: '?sort=name',
      state: { id: 2 }
    }} />
    

    state可以传递任何类型的参数,而且类似于post方式,可以不以明文传输。在跳转的页面中实用this.props.location.state获取数据。

    • replace:基本不用,为true时会替换掉上次访问的地址,也就是使用浏览器的回退按钮无法返回上一个页面。

    NavLink很明显就是为页面导航准备的,因为导航是要有激活状态的。

    • activeClassName:激活时要渲染的样式名
    <NavLink to="/mime" activeClassName='actvived'>mime</NavLink>
    
    • activeStyle:直接把激活的样式写在后面
    <NavLink to="/mime" activeStyle={{color: red,fontSize: 20px}}>mime</NavLink>
    
    • exact:如果为true,只有当访问地址严格匹配时才会使用激活样式。

    • strict:如果为true,只有挡访问地址后的斜杠严格匹配(有或没有)才会使用激活样式。

    • isActive:跟一个函数,当导航激活时要做的事情。

    <StaticRouter>

    api中介绍说这是一个从来不会改变位置的Router。当用户没有实际点击时,因此位置也从未发生变化。这在服务端渲染很有用,因为在后台上, 我们只会渲染一次,而且不会直接地对用户的交互操作做出反应。

    看下API中的案例:

    import { createServer } from 'http'
    import React from 'react'
    import ReactDOMServer from 'react-dom/server'
    import { StaticRouter } from 'react-router'
    
    createServer((req, res) => {
      // 这个context包含渲染的结果
      const context = {}
      const html = ReactDOMServer.renderToString(
        <StaticRouter location={req.url} context={context}>
          <App/>
        </StaticRouter>
      )
    
      // 如果使用<Redirect>,context.url将包含要重定向到的URL
      if (context.url) {
        res.writeHead(302, {
          Location: context.url
        })
        res.end()
      } else {
        res.write(html)
        res.end()
      }
    }).listen(3000)
    
    • basename:所有位置的基本url,正确的格式应该是前面有/后面没有/
    <StaticRouter basename="/main">
      <Link to="/list"/> <!--渲染出来应该是<a href="/main/list">-->
    </StaticRouter>
    
    • location:如果是个字符串就应该是服务器上收到的url(req.url)。如果是个对象应该是一个类似{pathname, search, hash, state}的位置对象。
    <StaticRouter location={{ pathname: '/home' }}>
      <App/>
    </StaticRouter>
    
    • context:一个普通js对象,在渲染过程中组件可以向对象里添属性以存储有关渲染的信息。

    我们使用它来找出渲染结果,context上下文对象是自己的,我们可以改变它。

    //app上有个404组件,把status改成404
    const NotFound = () => (
      <Route
        render={({ staticContext }) => {
          if (staticContext) {
            staticContext.status = 404;
          }
          return (
            <div>
              <h1>你输入的地址不正确哟!</h1>
            </div>
          );
        }}
      />
    );
    
    // 在服务端我们就可以通过判断,对这个404界面发送404响应
    const context = {};
    const content = renderToString(
        <Provider store={store}>
            <StaticRouter location={req.url} context={context}>
                <App />
            </StaticRouter>
        </Provider>
    );
    if (context.status === 404) {
        res.status(404);
    }
    

    或者判断重定向,如果有context.url就说明应用被重定向,这允许我们从服务器发送适当的重定向。

    if (context.url) {
      // can use the `context.status` that
      // we added in RedirectWithStatus
      redirect(context.status, context.url)
    }
    
    <Switch>

    用来包裹Route或者Redirect组件,而且不能放其他元素,用来渲染第一个匹配到的路由,不会向下继续匹配。如果不用Switch包裹的话,访问/about时下面三个组件都会被渲染。

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

    将匹配到的路径重定向到一个新的地址。新的地址应该覆盖掉访问的地址。

    <Route path="/game" component={Game}></Route>
    <Redirect from="/home" to="game" push={true}></Redirect>
    
    • from:匹配到的访问的地址

    • to:重定向到的地址

    • push:为true时,重定向到的地址会添加到历史访问记录,并且无法回到之前访问的地址。

    <withRouter>

    withRouter可以用来包装任何自定义组件,并将historylocationmatch三个对象传入。这样组件就可以拿到路由信息。

    import {withRouter} from 'react-router-dom';
    const Home = withRouter(({history,location,match})=>{
      return <h1>{location.pathname}</h1>
    })
    
    history

    表示浏览器历史记录的对象,可以对它进行一系列操作,有以下属性和方法:

    • length:获取历史堆栈中的条目数

    • action:获取当前操作(push/pop/replace)

    • location:获取当前位置,包括(pathname、search、hash、state、)

    • push(''):添加一个新的条目到历史堆栈(一般会用来跳转到一个新页面)

    • replace(''):替换掉当前堆栈上的条目

    • go(n):通过n个条目移动历史堆栈中的指针,向前或向后n个

    • goBack():后退一个历史条目,相当于go(-1)。

    • goForward():前进一个历史条目,相当于go(1)

    • block:防止导航

    match

    <Route path>如何与url匹配的信息,包含以下内容:

    • params:URL动态段解析对应的键值对

    • isExact:如果匹配整个URL则为true

    • path:<Route>匹配的路径

    • url:当前真正访问的路径

    location

    程序当前所在的位置。。。

    react-router-redux


    如果使用的react-router-redux(把react-router url的变化、参数等信息作为状态,交给redux的store管理),则可以直接从state中获取路由信息。

    import {routeMiddleware} from 'react-router-redux';
    const store = createStore(combineReducers, applyMiddleware(routeMiddleware));
    const mapStateToProps = (router) => ({
        pathname: rourter.location.pathname
    })
    export default connect(mapStateToProps)(MyControl)
    

    具体请看API

    react-router-config


    react-router-config是帮助我们配置静态路由的。算是比较好理解的,API只有两个方法:

    matchRoutes(routes, pathname)

    该方法接收两个参数(routes配置,请求路径),返回一个数组。

    import { matchRoutes } from 'react-router-config'
    const branch = matchRoutes(routes, '/child/23')
    

    这个返回的数组的每一个元素包含两个属性routes和match。结构如下:

    [
      {
        route: {
          path: '/app',
          component: [(Function: component)],
          routes: [Array]
        },
        match: { path: '/app', url: '/app', isExact: false, params: {} }
      },
      {
        route: { path: '/app/resources', component: [Object] },
        match: {
          path: '/app/resources',
          url: '/app/resources',
          isExact: true,
          params: {}
        }
      }
    ];
    

    这个方法在服务端貌似用的多一些,自己没有用过不说太多。。。

    renderRoutes(routes, extraProps = {})

    该方法接受两个参数:配置的路由和要传的值。
    写这篇文章的时候,react-router-config的renderRoutes方法并没有更新的npm上,其实已经实现了,不知道为什么没有上npm。看下源码:

    import React from 'react';
    import Switch from 'react-router/Switch';
    import Route from 'react-router/Route';
    
    const renderRoutes = (routes, extraProps = {}, switchProps = {}) =>
      (routes ? (
        <Switch {...switchProps}>
          {routes.map((route, i) => (
            <Route
              exact={route.exact}
              key={route.key || i}
              path={route.path}
              render={props =>
                (route.render ? (
                  route.render(props)
                ) : (
                  <route.component {...props} {...extraProps} route={route} />
                ))
              }
              strict={route.strict}
            />
          ))}
        </Switch>
      ) : null);
    

    实际上就是在一个<Switch>中创建了多个<Route>

    看一下API中给出的代码示例,路由可以进行统一的配置了,不过有个缺陷就是:在组件的render方法中还需要调用renderRoutes方法。

    import { renderRoutes } from 'react-router-config'
    // 配置路由
    const routes = [
      { component: Root,
        routes: [
          { path: '/',
            exact: true,
            component: Home
          },
          { path: '/child/:id',
            component: Child,
            routes: [
              { path: '/child/:id/grand-child',
                component: GrandChild
              }
            ]
          }
        ]
      }
    ]
    // 设置路由
    ReactDOM.render((
      <BrowserRouter>
        {renderRoutes(routes)}
      </BrowserRouter>
    ), document.getElementById('root'))
    // 在组件中调用方法
    const Root = ({ route }) => (
      <div>
        <h1>Root</h1>
        {renderRoutes(route.routes)}
      </div>
    )
    const Home = ({ route }) => (
      <div>
        <h2>Home</h2>
      </div>
    )
    const Child = ({ route }) => (
      <div>
        <h2>Child</h2>
        {renderRoutes(route.routes, { someProp: 'these extra props are optional' })}
      </div>
    )
    const GrandChild = ({ someProp }) => (
      <div>
        <h3>Grand Child</h3>
        <div>{someProp}</div>
      </div>
    )
    

    相关文章

      网友评论

          本文标题:react路由

          本文链接:https://www.haomeiwen.com/subject/orsymftx.html