react路由、NavLink组件封装

-

react路由其实是借助BOM中的History对象实现的,专门有一个history.js的库,可以让操作history对象简单起来。

用history.js可以通过两种方式创建路由对象:

1、History.createBrowserHistory()   // 直接使用h5推出的history身上的api   (有些老的浏览器会不支持)

2、History.createHashHistory()  // 直接使用原生的 hash值(锚点) (所有浏览器都支持)

创建好的对象上有 push、replace、back、forward方法,可以操作路由

historyObj.listen( location => {  console.log(location) }); 监听路由变化

 

react路由的插件库是 react-router-dom

下面以react-router-dom@5实验

整个应用需要用一个路由器包裹,否则每个路由器中的路由是独立的

import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'
import App from './App'

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

基本的路由配置

import React from 'react'
import { Link, Route } from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
import './App.css'

// 创建外壳组件App
class App extends React.Component {
  
  render() {
    return (
      <div className="container">
        <h1>react-router-dom-demo</h1>
        <div className="main">
          <aside className="aside">
            { /**
             * 编写路由链接
             * 在react中,靠路由连接切换组件
             */}
              <Link className="btn" to="/home">home</Link>
              <Link className="btn" to="/about">about</Link>
          </aside>
          <div className="content">
            {/**
             * 注册路由
             */}
              <Route path="/home" component={Home}></Route>
              <Route path="/about" component={About}></Route>
          </div>
        </div>
      </div>
    )
  }
}

export default App

 路由组件和一般组件的区别

1、接收props不同

路由组件会收到路由传给组件的props:history、location、match

2、写法不同   <Demo/>  <Route path="/demo" component={Demo} />

3、存放位置不同

一般组件放在components文件下,路由组件存放在pages或views文件下,名字自己定义

NavLink组件的使用:

import { NavLink } from 'react-router-dom'
<NavLink className="btn" activeClassName="active" to="/home">home</NavLink>

NavLink有个activeClassName属性,用来指定当前选中的类名,默认是active,如果是active可以省略

 NavLink组件的封装

NavLink 上需要传activeClassName 和 class,一个项目中的NavLink组件几乎传的这两个class都一样,把它封装成一个组件,就不用每个都传一遍了

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

export default class MyNavLink extends Component {
  render() {
    // const { title, children } = this.props;
    return (
      // <NavLink className="btn" activeClassName="active" to={to}>{title}</NavLink>
      // <NavLink className="btn" activeClassName="active" {...this.props}>{children}</NavLink>
      // 标签体内容是一个特殊的标签属性(children)
      // <NavLink className="btn" activeClassName="active" {...this.props} children={children} />
      // children也包含在了this.props中,children={children}可以省略
      <NavLink className="btn" activeClassName="active" {...this.props} />
    )
  }
}

使用:

<MyNavLink to="/home">home</MyNavLink>

 Switch的使用

         import { Switch } from 'react-router-dom'
        <Switch>
                <Route path="/home" component={Home}></Route>
                <Route path="/about" component={About}></Route>
                <Route path="/about" component={Test}></Route>
              </Switch>    

上面路由/about 对应了两个组件,如果未用Switch标签包裹起来,/about会同时展示 About、Test两个组件,用Switch标签包起来后,路由匹配到第一个组件后,就不会再往下匹配,/about只会匹配About组件。

BrowserRouter模式下,第三方css样式丢失问题,如果多级路由,css文件是用./相对路径引入的话,刷新页面会出现样式丢失的问题,因为相对路径会把路由的路径带上去,导致在public目录页下找不到资源,找不到资源的情况下会默认把index.html返回,解决办法有:1、把./相对路径改成绝对路径/,2、换成hash路由

 路由的模糊匹配与精准匹配

路由默认是模糊匹配

<MyNavLink to="/home/a/b">home</MyNavLink>
可以匹配
<Route path="/home" component={Home}></Route>

开启精准匹配:

        <Switch>
                <Route exact path="/home" component={Home}></Route>
                <Route path="/about" component={About}></Route>
                <Route exact={true} path="/home/a/b" component={Test}></Route>
              </Switch>

 精准匹配不要随便开启,只有不开精确匹配会导致错误时,才开启精确匹配,(开启精确匹配后,将无法访问子路由)

 路由重定向  Redirect  的使用

         import { Link, NavLink, BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
              <Switch>
                <Route path="/home" component={Home}></Route>
                <Route path="/about" component={About}></Route>
                <Route path="/home" component={Test}></Route>
                <Redirect to="/home"></Redirect>
              </Switch>

Redirect写在注册路由的最下方,当没有匹配到任何路由时,会重定向到 Redirect指定的路由

 嵌套路由

在/home路由下写两个子路由

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

export default class Home extends Component {
  render() {
    console.log('home组件收到的props是:', this.props);
    return (
      <div>
        <h1>我是Home的内容</h1>
        <ul className="tab">
          <li className="tab-items">
            <MyNavLink to="/home/news">news</MyNavLink>
          </li>
          <li className="tab-items">
            <MyNavLink to="/home/message">message</MyNavLink>
          </li>
        </ul>
        {/** 注册路由 */}
        <Switch>
          <Route path="/home/news" component={News}></Route>
          <Route path="/home/message" component={Message}></Route>
          <Redirect to="/home/news"></Redirect>
        </Switch>
      </div>
    )
  }
}

向路由组件传递params参数

1、传递

2、声明

3、接收

import React, { Component } from 'react'
import { Link, NavLink, BrowserRouter, Route, Switch, Redirect } 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}>
                  {/** 向路由组件传递params参数 */}
                  <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
                </li>
              )
            })
          }
        </ul>
        <hr />
        {/** 声明接收params参数 */}
        <Route path="/home/message/detail/:id/:title" component={Detail}></Route>
      </div>
    )
  }
}

接收

import React, { Component } from 'react'
const Detaildata = [
  { id: '01', content: '你好,中国' },
  { id: '02', content: '你好,尚硅谷' },
  { id: '03', content: '你好,未来的自己' },
]

export default class Detail extends Component {
  render() {
    console.log(this.props, 'detail的props');
    // 接收params参数
    const { id, title } = this.props.match.params;
    const findResult = Detaildata.find(item => item.id === id);
    return (
      <ul>
        <li>ID: {id}</li>
        <li>TITLE: {title}</li>
        <li>CONTENT: {findResult.content}</li>
      </ul>
    )
  }
}

 向路由传递search参数

1、传递search参数无需声明

2、接收search参数时需要把查询字符串转换成key-value形式的对象,取值方便,这里借用了一个库 qs 老版本用 querystring,是react脚手架自带的库;

{/* 向路由组件传递search参数 */}
<Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
{/* search参数无需声明接收 */}
<Route path="/home/message/detail" component={Detail}></Route>

接收search参数
import qs from 'qs' // urlencode和object转换
const { search } = this.props.location;
const { id, title } = qs.parse(search.slice(1));

 向路由组件传递state参数

1、传递state参数,需要是个对象的形式

2、接收时再props.location.state中获取

{/* 向路由传递state参数  刷新页面时state参数不会丢失,因为state维护在history对象上,如果换了浏览器,此参数会丢失 */}
<Link to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link>
{/* state参数无需声明接收 */}
<Route path="/home/message/detail" component={Detail}></Route>
// 接受state参数
const { id, title } = this.props.location.state || {};

 路由开启replace模式

路由默认是push模式,会留下记录,可以前进后退,如果开启replace模式,就不会有记录了

<Link replace to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link>
// 或者
<Link replace={true} to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link>

 编程式路由导航

import React, { Component } from 'react'
import { Link, NavLink, BrowserRouter, Route, Switch, Redirect } 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' },
    ]
  }
  // repalce跳转
  replaceShow = (id, title) => {
    // replace跳转+携带params参数
    // this.props.history.replace(`/home/message/detail/${id}/${title}`)

    // replace跳转+携带query参数
    // this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)

    // replace跳转+携带state参数
    this.props.history.replace(`/home/message/detail`, { id, title })
  }
  // push跳转
  pushShow = (id, title) => {
    // push跳转+携带params参数
    // this.props.history.push(`/home/message/detail/${id}/${title}`)

    // push跳转+携带query参数
    // this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)

    // push跳转+携带state参数
    this.props.history.push(`/home/message/detail`, { id, title })
  }
  // 回退
  back = () => {
    this.props.history.goBack()
  }
  // 前进
  forword = () => {
    this.props.history.goForward()
  }
  // go
  go = () => {
    this.props.history.go(-1); // 负数代表后退几步,正数代表前进几步
  }
  render() {
    const { messageArr } = this.state;
    return (
      <div>
        <ul>
          {
            messageArr.map(msgObj => {
              return (
                <li key={msgObj.id}>
                  {/** 向路由组件传递params参数 */}
                  <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
                  {/* 向路由组件传递search参数 */}
                  {/* <Link to={`/home/message/detail?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}
                  {/* 向路由传递state参数  刷新页面时state参数不会丢失,因为state维护在history对象上,如果换了浏览器,此参数会丢失 */}
                  {/* <Link replace to={ { pathname: '/home/message/detail', state: { id: msgObj.id, title: msgObj.title } } }>{msgObj.title}</Link> */}
                  <button onClick={() => this.pushShow(msgObj.id, msgObj.title)}>push查看</button>
                  <button onClick={() => this.replaceShow(msgObj.id, msgObj.title)}>replace查看</button>
                </li>
              )
            })
          }
        </ul>
        <hr />
        {/** 声明接收params参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail}></Route> */}
        {/* search参数无需声明接收 */}
        {/* <Route path="/home/message/detail" component={Detail}></Route> */}
        {/* state参数无需声明接收 */}
        <Route path="/home/message/detail" component={Detail}></Route>
        <hr />
        <button onClick={ this.back }>回退</button>
        <button onClick={ this.forword }>前进</button>
        <button onClick={ this.go }>go</button>
      </div>
    )
  }
}

 withRouter的使用

一般组件像使用路由跳转的功能,需要用withRouter加工一下,使一般组件拥有路由组件的api

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

class Header extends Component {
  // 回退
  back = () => {
    this.props.history.goBack()
  }
  // 前进
  forword = () => {
    this.props.history.goForward()
  }
  // go
  go = () => {
    this.props.history.go(-1); // 负数代表后退几步,正数代表前进几步
  }
  render() {
    console.log(this.props, 'Header组件的props');
    return (
      <h3>
        react-router-dom-demo
        <button onClick={ this.back }>回退</button>
        <button onClick={ this.forword }>前进</button>
        <button onClick={ this.go }>go</button>
      </h3>
    )
  }
}
// withRouter可以加工一般组件,让一般组件拥有路由组件特有的api(props:{ history, location, match })
// withRouter的返回值是一个新组件
export default withRouter(Header)

 BrowserRouter和HashRouter的区别

 

 

 

 

 

 

-

posted @ 2022-10-07 18:09  古墩古墩  Views(249)  Comments(0Edit  收藏  举报