React 路由组件 详解

Posted YuLong~W

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React 路由组件 详解相关的知识,希望对你有一定的参考价值。

路由组件

路由组件与一般组件区别:

1、写法不同:

  • 一般组件:<Demo/>
  • 路由组件:<Route path="/demo" component={Demo}/>

2、存放位置不同:

  • 一般组件:components目录
  • 路由组件:pages目录

3、接收到的props不同:

  • 一般组件:写组件标签时传递了什么,就能收到什么
  • 路由组件:接收到三个固定的 属性
    • history:
      • go: ƒ go(n)
      • goBack: ƒ goBack()
      • goForward: ƒ goForward()
      • push: ƒ push(path, state)
      • replace: ƒ replace(path, state)
    • location:
      • pathname: “/about”
      • search: “”
      • state: undefined
    • match:
      • params: {}
      • path: “/about”
      • url: “/about”

1、HashRouter和BrowserRouter

其实就是路由的hash和history两种模式,并且这两个组件是路由的容器,必须在最外层

 // hash模式
ReactDOM.render(
	 <HashRouter>
      	<Route path="/" component={Home}/>
</HashRouter>
)

// history模式
ReactDOM.render(
	   <BrowserRouter>
      	 <Route path="/" component={Home} />
      </BrowserRouter>
)

区别:

  • 底层原理不一样
    • BrowserRouter使用的是H5的history API,不兼容IE9及以下版本
    • HashRouter使用的是URL的哈希值
  • path表现形式不一样
    • BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
    • HashRouter的路径包含#,例如:localhost:3000/#/demo/test
  • 刷新后对路由state参数的影响
    • BrowserRouter没有任何影响,因为state 保存在history对象中
    • HashRouter刷新后会导致路由state 参数的丢失
  • 备注:HashRouter可以用于解决一些路径错误相关的问题

2、Route

路由的一个原材料,它是控制路径对应显示的组件

Route的参数:

  • path:跳转的路径
  • component: 对应路径显示的组件
  • render:可以自己写render函数返回具体的dom,而不需要去设置component
  • location: 传递route对象,和当前的route对象对比,如果匹配则跳转
  • exact: 匹配规则,true的时候则精确匹配

3、Router

低级路由,适用于任何路由组件,主要和redux深度集成,使用必须配合history对象,使用Router路由的目的是和状态管理库如redux中的history同步对接

<Router history={history}>
    ...
</Router>

4、Link和NavLink

两者都是跳转路由,NavLink的参数更多些

Link:

  • to: 有两种写法,表示跳转到哪个路由
  • 字符串写法: <Link to="/a" />
  • 对象写法: <Link to={{ pathname: '/courses', search: '?sort=name', hash: '#the-hash', state: { fromDashboard: true } }}/>
  • replace:就是将push改成replace
  • innerRef:访问Link标签的dom

NavLink:

  • Link的所有参数

  • activeClassName: 路由激活的时候设置的类名 实现路由链接的高亮

  • activeStyle :路由激活设置的样式

  • exact: 参考Route,符合这个条件才会激活active类

  • strict: 参考Route,符合这个条件才会激活active类

  • isActive: 接收一个回调函数,active状态变化的时候回触发,返回false则中断跳转

    const oddEvent = (match, location) => {
      console.log(match,location)
      if (!match) {
        return false
      }
      console.log(match.id)
      return true
    }
    
    <NavLink isActive={oddEvent} to="/a/123">组件一</NavLink>
    
  • location: 接收一个location对象,当url满足这个对象的条件才会跳转

    <NavLink to="/a/123" location={{ key:"mb5wu3", pathname:"/a/123" }}/>
    

NavLink的使用:

在之前的效果展示中,Link组件不会进行高亮显示,因此改成NavLink用法

App.js文件修改的代码:

{/* 在React中靠路由链接实现切换组件 */}
<NavLink activeClassName="add" className="list-group-item" to="/home">Home</NavLink>
<NavLink activeClassName="add" className="list-group-item" to="/about">About</NavLink>

//这里用 activeClassName="add" 来控制按钮高亮的颜色显示

index.html 文件修改后的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./css/bootstrap.css">
    <style>
        .add{
            background-color: rgb(209,137,4) !important; // 因为 bootstrap 的权重比较高,所以要用!important
            color:white !important;
        }
    </style>
</head>
<body>
    <div id="root"></div>
</body>
</html>

NavLink的封装:

因为在 App.js 文件中写 NavLink 太长了, 所以在这里把 NavLink 单独提出来写 MyNavLink 一个组件, 使用时调用它,可以使代码更加简洁

MyNavLink组件代码:

import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'

export default class MyNavLink extends Component {
    render() {
        return (
           <NavLink activeClassName="add" className="list-group-item" {...this.props} />
        )
    }
}

App.js修改后的代码:

{/* 在React中靠路由链接实现切换组件 */}
<MyNavLink to="/home">Home</MyNavLink>
<MyNavLink to="/about">About</MyNavLink>

进行封装的知识点:

  • 标签体内容是一个特殊的标签属性children
  • 通过this.props.children可以获取标签体内容

问题:

1、解决多级路径刷新页面样式丢失

如果匹配的路径不对, 就会引发css样式的丢失问题

默认执行index.html文件

App.js代码修改:

{/* 在React中靠路由链接实现切换组件 */}
<MyNavLink to="/aaa/home">Home</MyNavLink>
<MyNavLink to="/aaa/about">About</MyNavLink>

{/* 注册路由 路由组件写法 */}
<Switch>
 	<Route path="/aaa/home" component={Home}/>
	<Route path="/aaa/about" component={About}/>
</Switch>

效果:

解决方法:

  • public/index.html 中 引入样式时不写 ./ 写 / (常用)
  • public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)
  • 使用HashRouter

2、路由的严格匹配与模糊匹配

  • 1.默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
  • 2.开启严格匹配:<Route exact={true} path="/about" component={About}/>
  • 3.严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

App.js代码修改:

{/* 在React中靠路由链接实现切换组件 */}
<MyNavLink to="/home">Home</MyNavLink>
<MyNavLink to="/aaa/about">About</MyNavLink>

{/* 注册路由 路由组件写法 */}
<Switch>
 	<Route exact path="/home" component={Home}/>
	<Route exact path="/about" component={About}/>
</Switch>

效果:

5、Redirect

页面重定向:一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

<Switch>
	<Route path="/about" component={About}/>
	<Route path="/home" component={Home}/>
	<Redirect to="/about"/>
</Switch>

用法:

// 基本的重定向
<Redirect to="/somewhere/else" />

// 对象形式
<Redirect
  to={{
    pathname: "/login",
    search: "?utm=your+face",
    state: { referrer: currentLocation }
  }}
/>

// 采用push生成新的记录
<Redirect push to="/somewhere/else" />

// 配合Switch组件使用,form表示重定向之前的路径,如果匹配则重定向,不匹配则不重定向
<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>

6、Switch

路由切换,只会匹配第一个路由,可以想象成tab栏。Switch内部只能包含Route、Redirect和Router。

通常情况下,path和component是一一对应的关系。Switch可以提高路由匹配效率(单一匹配)。

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

7、withRouter

当一个非路由组件也想访问到当前路由的match、location、history对象,那么withRouter将是一个非常好的选择,可以理解为将一个组件包裹成路由组件

  • withRouter可以加工一般组件, 让一般组件具备路由组件所特有的API
  • withRouter的返回值是一个新组件
import { withRouter } from 'react-router-dom'
const MyComponent = (props) => {
    const { match, location, history } = this.props
     return (
        <div>{props.location.pathname}</div>
    )
}
const FirstTest = withRouter(MyComponent);

示例:

效果:

Header 组件代码:

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'

 class Header extends Component {
    // 回退
    back = () => {
        this.props.history.goBack()
    }

    // 前进
    forward = () => {
        this.props.history.goForward()
    }

    /// go
    go = () => {
        this.props.history.go(2)
    }

    render() {
        // console.log('一般组件',this.props)
        return (
            <div className="page-header">
                <h2>React Router Demo</h2>
                <button onClick={this.back}>回退</button>
                <button onClick={this.forward}>前进</button>
                <button onClick={this.go}>go</button>
            </div>
        )
    }
}

export default withRouter(Header)

嵌套路由

1.注册子路由时要写上父路由的path值

2.路由的匹配是按照注册路由的顺序进行的

News 组件代码:

import React, { Component } from 'react'

export default class News extends Component {
    render() {
        return (
            <ul>
                <li>news001</li>
                <li>news002</li>
                <li>news003</li>
            </ul>
        )
    }
}

Message 组件代码:

import React, { Component } from 'react'

export default class Message extends Component {
    render() {
        return (
            <div>
                <ul>
                <li>
                    <a href="/message1">message001</a>&nbsp;&nbsp;
                </li>
                <li>
                    <a href="/message2">message002</a>&nbsp;&nbsp;
                </li>
                <li>
                    <a href="/message/3">message003</a>&nbsp;&nbsp;
                </li>
                </ul>
            </div>
        )
    }
}

Home组件代码:

import React, { Component } from 'react'
import MyNavLink from '../../components/MyNavLink'
import { Route,Switch } from 'react-router-dom'
import News from './News'
import Message from './Message'

export default class Home extends Component {
    render() {
        console.log('路由组件', this.props)
        return (
            <div>
                <h3>我是Home的内容</h3>
                <div>
                    <ul class="nav nav-tabs">
                        <li>
                            {/* 注册子路由时要写上父路由的path值 */}
                            <MyNavLink to="/home/news">News</MyNavLink>
                        </li>
                        <li>
                            <MyNavLink to="/home/message">Message</MyNavLink>
                        </li>
                    </ul>
                    {/* 注册路由 */}
                    <Switch>
                        {/* 注册子路由时要写上父路由的path值 */}
                        <Route path="/home/news" component={News}/>
                        <Route path="/home/message" component={Message}/>
                    </Switch>
                </div>
            </div>
        )
    }
}

向路由组件传递参数


  • params参数
    • 路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>
    • 注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>
    • 接收参数:this.props.match.params

  • search参数
    • 路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>
    • 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
    • 接收参数:this.props.location.search
    • 备注:获取到的search是urlencoded编码字符串,需要借助querystring解析

  • state参数
    • 路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
    • 注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
    • 接收参数:this.props.location.state
    • 备注:刷新也可以保留住参数

示例:

Message 组件代码:

import React, { Component } from 'react'
import {Link, Route} from 'react-router-dom'
import Detail from './Detail'

export default class Message extends Component {
    state = {
        messageArr:[
            {id:'01',title:'消息1'},
            {id:'02',title:'消息2'},
            {id:'03',title:'消息3'},
        ]
    }

    render() {
        const { messageArr } = this.state;
        return (
            <div>
                <ul>
                    {
                        messageArr.map((msgObj)=>{
                            return (
                                <li key={msgObj.id}>
                                  {/* 1、向路由组件传递params参数 */}
                                  {/* 
                                  <Link 
                                      to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}
                                  </Link> 
                                  */}

                                  {/* 2、向路由组件传递search参数 */}
                                  {/* 
                                  <Link 
                                  	to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}
                                  </Link> 
                                  */}

                                  {/* 3、向路由组件传递state参数 */}
                                  <Link 
        							to={{pathname:"/home/message/detail",state:{id:msgObj.id,title:msgObj.title}>{msgObj.title}
								  </Link>
                                </li>
                            )
                        })
                    }
                </ul>
                <hr />

                {/* 1、声明接收params参数 */}
                {/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}

                {/* 2、params参数无需声明接收, 正常注册即可 */}
                {/* <Route path="/home/message/detail" component={Detail}/> */}

                {/* 3、state参数无需声明接收, 正常注册即可 */}
                <Route path="/home/message/detail" component={Detail}/>
            </div>
        )
    }
}

Detail 组件代码:

 import React, { Component } from 'react'
// import qs from 'querystring'

const DetailData = [
    {id:'01',content:'你好,中国'},
    {id:'02',content:'你好,世界'},
    {id:React路由的详解

React路由的详解

React路由的详解

使用具有不同片段字段的相同中继根查询的多个 react-router-relay 路由

React路由超详解...

React路由详解