React路由

react-router React路由,让父组件动态去挂载不同的子组件,本文以4.x为例;
4.x 对依赖包的划分:

  • react-router 路由基础库
  • react-router-dom 适用于浏览器环境的再次封装
  • react-router-native 适用于原生开发环境的再次封装
  • react-router-config 静态路由配置助手

v4v5用法差不多,但与v2/v3差异较大,v4/v5采用的是动态路由,而v2/v3采用的是静态路由。
v5 新增了一些新特性,向后兼容:

  • 消除了严格模式的警告
  • react v16有了更好的支持
  • 升级了react context api
  • 预优化build,无需考虑生产环境和开发环境

react-router 主张 一切皆组件,不像vue-router那样做中心化配置(集中式路由)。当然,react也支持集中式路由。

  • 前端路由的实现:history库,管理浏览器会话历史的工具库。本质上还是包装了原生BOM中的 window.historywindow.location.hash
  • 核心组件:<BrowserRouter>、<HashRouter>、<Route>、<Redirect>、<Link>、<NavLink>、<Switch>
    • Hash模式:<HashRouter>
    • HTML5:history模式:<BrowserRouter>
    • react native开发使用的是 MemoryHistory
  • 对象:history、match、withRouter

引入路由之后,可以把组件分为两类:普通组件(components目录)、路由组件(views目录)

基本配置

  1. 安装react-router-dom模块:npm install react-router-dom -S
  2. 在根组件App.js中引入路由相关的组件,加载的子组件包括Home.js、News.js、Product.js、NotFound.js
    import { BrowserRouter, Route, NavLink, Switch } from 'react-router-dom'
    render() {
        return(<BrowserRouter>
            <div>
                <header>
                    <NavLink to="/">首页</NavLink>
                    <NavLink to="/news">新闻</NavLink>
                    <NavLink to="/product">商品</NavLink>
                    <NavLink to="/notfound">一个不存在的路由</NavLink>
                </header>
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route path="/news" component={News} />
                    <Route path="/product" component={Product} />
                    <Route component={NotFound} />  // 404组件
                </Switch>
            </div>
        </BrowserRouter>)
    }
    
    • 在根组件中,把Home组件的路由配置为path="/",表示默认加载Home组件;
    • exact:表示严格匹配模式,必须完全匹配,如果设置为false,每次都会匹配 path="/" 的路由;
    • 404组件不需要声明任何path,且必须放在最后面,当上面的所有路由都没有匹配时,则匹配404路由。
  3. Redirect 表示重定向,可用于指定默认路由
    import { BrowserRouter, Route, NavLink, Switch, Redirect } from 'react-router-dom'
    render() {
        return(<BrowserRouter>
            <div>
                <header>
                    <NavLink to="/">首页</NavLink>
                    <NavLink to="/news">新闻</NavLink>
                    <NavLink to="/product">商品</NavLink>
                </header>
                <Switch>
                    <Route path="/home" component={Home} />
                    <Route path="/news" component={News} />
                    <Route path="/product" component={Product} />
                    <Redirect to="/home" />  //默认路由,加载Home组件
                </Switch>
            </div>
        </BrowserRouter>)
    }
    
  4. 一个项目中只有一个<BrowserRouter>,所以 <BrowserRouter> 包裹根组件即可!
  5. <NavLink><Route>必须包裹在<BrowserRouter>中,且<BrowserRouter>只能有一个直接子节点;
  6. NavLinkLink:它们最终都被解析为HTML中的<a>,但内部会阻止浏览器的默认行为;
    • to 属性用于指定跳转的路由,也可以是一个对象
      <Link to={{ pathname: '/courses', search: '?sort=name', hash: '#the-hash', state: { fromDashboard: true } }}/>
      
    • <NavLink><Link>的一个特定版本,它会在匹配上当前的url时给已经渲染的元素添加参数;
    • <NavLink> 上的属性:activeClassName、activeStyle、exact、strict ...
      <NavLink className="normal" activeClassName="active" to="/news">
      //NavLink的默认样式className="",匹配时的激活样式:activeClassName="" 或 activeStyle={}
      
    • 自定义NavLinkMyNavLink.jsx,统一使用一个activeClassName
      import React, {Component} from 'react'
      import {NavLink} from 'react-router-dom'
      export default class MyNavLink extends Component {
          render() {
              return <NavLink {...this.props} activeClassName='active'>
          }
      }
      
  7. Routepath指定路由,component指定路由对应的组件;
  8. Switch:从上往下匹配时,只要匹配成功,就不会再向下匹配,即只显示与当前路由匹配的Route
  9. <NavLink><Route>可以不在同一个组件中,只要路由匹配,都可以实现路由跳转。

路由传值

  1. 动态路由传参:<Route path="/info/:id" component={Info} />
    this.state = { id:12 }
    
    1. :id 表示占位符,对应的NavLink
      <NavLink to={`/info/${this.state.id}`}>详情</NavLink>
      
    2. Info.js 组件中获取动态路由的参数idthis.props.match.params
      componentDidMount() {
          let { id } = this.props.match.params
      }
      
  2. GET方式传参
     <Route path="/info" component={Info} />
     <NavLink to={`/info?id=${this.state.id}`}>详情</NavLink>
    
    1. Info.js 组件中,获取参数的对象:this.props.location
       const { search } = this.props.location
      
    2. 但是,GET参数并没有被解析,仍是一个原始字符串:?id=12
    3. 借助第三方模块url解析get参数:npm install url --save
      import url from 'url'  //url模块是node上的内置模块,npm上的url模块并不是node的
      componentDidMount() {
          let { id } = url.parse(this.props.location.search, true).query
      }
      
    4. React不建议使用 ?xxx=xxx的方式传参,而是把 to 设置为对象
      <NavLink to={ { pathname:'/info', id:this.state.id } }>详情</NavLink>
      
  3. React解析原始HTML字符串:dangerouslySetInnerHTML属性
    this.state = { content: '<p>原始HTML字符串</p>' }
    <div dangerouslySetInnerHTML={{__html: this.state.content}}></div>
    

路由嵌套

  1. User.js 组件是根路由 App.js 的动态子组件,路由名称为/user
    1. User组件中再配置两个动态子组件:Main.jsInfo.js
      import { Route, NavLink, Redirect, Switch } from 'react-router-dom'
      render() {
          return(<div>
              <div className="left">
                  <NavLink to="/user/">个人中心</NavLink>
                  <NavLink to="/user/info">用户详情</NavLink>
              </div>
              <div className="right">
                  <Switch>
                      <Route exact path="/user/" component={Main} />
                      <Route path="/user/info" component={Info} />
                  <Switch>
              </div>
          </div>)
      }
      
    2. User组件的路由为/user,则把子组件Main的路由设置为/user/,表示默认加载Main组件;
    3. 亦或者,Redirect 重定向的方式实现默认加载的路由组件
      render() {
          return(<div>
              <div className="left">
                  <NavLink to="/user/center">个人中心</NavLink>
                  <NavLink to="/user/info">用户详情</NavLink>
              </div>
              <div className="right">
                  <Switch>
                      <Route exact path="/user/center" component={Main} />
                      <Route path="/user/info" component={Info} />
                      <Redirect to='/user/center' />
                  <Switch>
              </div>
          </div>)
      }
      
    4. Redirectto 属性也可以是一个对象:pathname表示路由地址,其他的是参数;
      <Redirect to={ {pathname:'/', state:{foo:'bar'}} } />
      
  2. 动态获取父级的路由,再拼接成自己组件的路由
    <Route exact path={`${this.props.match.url}/`} component={Main} />
    <Route path={`${this.props.match.url}/info`} component={Info} />
    

JS控制路由跳转

  1. 借助对象:this.props.history 中的方法
  2. push()、replace()、goBack()、goForward() ......
    this.props.history.push('/home/user');
    this.props.history.push({ pathname: '/home/user', state: { foo:'bar' } });
    
  3. 获取传递的参数:this.props.location
    const { foo } = this.props.location.state;
    

路由守卫

  1. 路由守卫其实就是路由拦截,可以做权限控制,它其实也是一个组件,返回值是一个Route组件;
    class RouteGuard extends Component {
        state = {
            isLogin: false
        }
        render() {
            const {component:Component, ...otherProps} = this.props
            return (
                <Route {...otherProps} render={props => (
                    //只有已经登录了才允许进入守卫路由对应的组件,没有登录则重定向到登录页
                    this.state.isLogin ? <Component {...props}></Component> : 
                    (<Redirect to={{ pathname:'/login', state:{from:props.location.pathname} }} />)
                )}></Route>
            )
        }
    }
  1. 在需要控制权限的地方,应用路由守卫
    render() {
        return(<BrowserRouter>
            <div>
                <header>
                    <NavLink to="/">首页</NavLink>
                    <NavLink to="/news">新闻</NavLink>
                    <NavLink to="/mine">我的</NavLink>
                </header>
                <Switch>
                    <Route exact path="/" component={Home} />
                    <Route path="/news" component={News} />
                    <RouteGuard path="/mine" component={Mine} />  // 应用路由守卫
                </Switch>
            </div>
        </BrowserRouter>)
    }

路由的模块化

  1. 用一个数组管理项目中的所有路由,在把这个数组放在一个独立的JS文件中
    1. router.js 中引入相关组件,管理路由
      const routes = [
          { path:'/', component:Home, exact:true },
          { path:'/user', component:User }
      ]
      export default routes;
      
    2. 在根组件App中引入路由模块
      import routes from './route/router.js'
      render(){
          return(<BrowserRouter>
              <header>
                  <NavLink to="/">首页</NavLink>
                  <NavLink to="/user">用户</NavLink>
              </header>
              {
                  routes.map((route, key) => {
                      if(route.exact) {
                          return <Route key={key} exact path={route.path} component={route.component} />
                      } else {
                          return <Route key={key} path={route.path} component={route.component} />
                      }
                  })
              }
          </BrowserRouter>)
      }
      
  2. 嵌套路由的管理
    1. router.js
      const routes = [
          { path:'/', component:Home, exact:true },
          { path:'/user', component:User,
              routes:[
                  { path:'/user/', component:Main }, 
                  { path:'/user/info', component:Info }
              ]
          }
      ]
      
    2. 在根组件App中,通过<Route>render 属性,把子路由传递给子组件
      <Route key={key} path={route.path} render={props => (
          <route.component {...props} routes={route.routes} />
      )} />
      
    3. User组件中获取子路由:this.props.routes
      render() {
          return(<div>
              <div className="left">
                  <NavLink to="/user/">个人中心</NavLink>
                  <NavLink to="/user/info">用户详情</NavLink>
              </div>
              <div className="right">
                  {
                      this.props.routes.map((route, key) => {
                          return <Route key={key} exact path={route.path} component={route.component} />
                      })
                  }
              </div>
          </div>)
      }
      
  3. 子路由的<Route />上可以都加上 exact 属性,而根路由的<Route />上不行!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容