react笔记_10react路由

什么叫做路由?

一个路由就是一个映射关系(key:value)
key为路径, value可能是function或component

  • function: 后端路由,用来处理客户端提交的请求;
  • component:前端路由,用于展示页面内容;

路由组件与一般组件区别

  • 写法不同:
    • 一般组件:<Demo/>
    • 路由组件: <Route path="/demo" component={Demo}/>
  • 存放位置不同:
    • 一般组件: components
    • 路由组件: pages
  • 接收到的props不同:
    • 一般组件: 写组件标签时传递了什么,就能收到什么
    • 路由组件: 接收带三个固定的属性
      • history
      • location
      • match

react中配置路由

在react中使用 react-router-dom(插件) 来进行路由的配置

[1] 下载

使用npm i react-router-dom 命令下载 路由插件;

  • 报错:A is only ever to be used as the child of element, never rendered directly. Please wrap your in a .
  • 原因:我下载时使用的命令是npm i react-router-dom 下载的是最新版本(6版本)—语法不兼容了
  • 解决:npm i react-router-dom@5.2.0 下载6以下的版本
[2] 注册路由

在下载了 react-router-dom 插件之后,就可以通过 react-router-dom包引入 Route组件注册路由。

Route组件包含以下属性

  • path: 路由的路径
  • component: 组件
  • exact: 是否进行严格匹配(默认模糊匹配)

以下就注册了一个 Home 组件 ,组件路径为/home。

// [1]引入注册路由组件
import {Route} from 'react-router-dom'
// [2] 引入组件
import Home from '../src/pages/Home'
// [3] 注册组件
<Route path='/home' component={Home}></Route>
Switch

在注册路由时如下:可能有两个甚至多个的路由注册,通过 下面代码 注册路由 当path变为/home时 路由出口的位置展示哪个组件-- Home组件orTest组件or两个都展示?

 <Route path='/about' component={About}/>
 <Route path='/home' component={Home}/>
 <Route path='/home' component={TEst}/>
  • 答案:两个都展示
  • 原因:路由的匹配规则为-> 从第一个开始匹配直至最后一个,即使找到对应路由也不会停止寻找
  • 问题:正常情况下,我们配置路由和路径时是 一一对应 的关系,因此这样的匹配规则是非常浪费性能的;
  • 解决:将 注册路由的代码 使用 switch 组件包裹 ;
  • 使用switch包裹后的匹配规则: 从第一个开始寻找,找到一个对应的路由则匹配结束;

通过 下面代码 注册路由 当path变为/home时仅仅展示Home组件

 <Switch>
   <Route path='/about' component={About}/>
   <Route path='/home' component={Home}/>
   <Route path='/home' component={Nested}/>
</Switch>
Route组件的三个互斥属性

组件的渲染存在三种方式

  • component
  • render
  • children

若是以上三个属性同时存在 则优先级 children > component > render

component

component属性用于渲染对应组件,属性值为一个组件

<Route path='/about' component={About}/>
  • 进行路由匹配 若是跳转路径为 /about 就渲染 About组件
render

render属性也是用于渲染组件,属性值为一个函数,一般用在一些渲染组件的之前或者之后做一些事情,例如 一些需要登陆权限之类的受保护的组件。

<Route path='/about' render={(props)=>{
  if(token){
    return <About />
  }else{
    return <Redirect to='/login' />
  }
}}/>
  • 进行路由匹配,若是路径为/about就会自动调用render函数,得到的值就是需要渲染的组件
children

children用法跟 render 一样,区别在于,就算不匹配路由,children 函数也会触发;

[3] 导航区(路由跳转)

在下载了 react-router-dom 插件之后,就可以通过 react-router-dom包引入 LinkNavLink 组件进行导航(路由跳转)。

Link组件包含以下属性

  • to: 点击之后需要跳转的路由路径
  • replace:是否覆盖当前路径

NavLink组件包含以下属性

  • to: 点击之后需要跳转的路由路径
  • activeClassName: 点击之后的样式的类名,默认值为active
  • replace:是否覆盖当前路径

以下就注册了一个 Home 组件 ,组件路径为/home;配置了一个跳转到home组件的链接,当点击“点我跳转到home组件”页面路径就变为/home了。

import {Component} from 'react'
import Home from '../src/pages/Home'
import {Link, Route} from 'react-router-dom'
export default class App extends Component {
  render(){
    return (
      <div>
        <Link to='/home'>点我跳转到home组件</Link>
        <Route path='/home' component={Home}></Route>
      </div>
    )
  }
}
编程式导航

除了上述通过组件进行路由跳转,也可以通过编程式导航进行路由跳转;

语法-借助prop.history对象上的API对操作路由跳转、前进、后退

this.prop.history.push()
this.prop.history.replace()
this.prop.history.goBack()
this.prop.history.goForward()
this.prop.history.go()
在新窗口打开页面
const domA = document.createElement('a')
domA.href = history.createHref({ pathname: url })
domA.target = '_blank'
domA.click()
在不刷新页面的情况下去掉路径上的参数

因为路径上携带参数是添加到props中的,而props值改变不会重新渲染页面,因此在跳转到页面之后,我们可以在useEffect中直接 跳转即可

  useEffect(()=>{
   goodsList()
   // 若是存在参数,直接跳转到当前页面
   if(searchparams.order_sn){
     props.history.push('/admin/sellorder')
   }
 },[])
[4] 路由出口

路由分为两类:

路由出口

  • 若是Browser路由则路由出口为 <BrowserRouter>;
  • 若是Hash路由则路由出口为 <HashRouter>;

下面配置表示该项目使用的是 Browser路由:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <BrowserRouter>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </BrowserRouter>
);

下面配置表示该项目使用的是 Hash路由:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { HashRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <HashRouter>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </HashRouter>
);

若是不添加路由出口会报如下错误:
在这里插入图片描述

[5] 路由重定向
 <Switch>
   <Route path='/about' component={About}/>
   <Route path='/home' component={Home}/>
   <Route path='/nested' component={Nested}/>
</Switch>

以上是注册的几个路由,但是一进入页面时,匹配不到任何的路由,此时若是我们想进入页面(url为空)时默认展示home组件的内容,可以使用路由重定向。
Redirect组件包含以下属性

  • to: 路由重定向的路径

在下载了 react-router-dom 插件之后,就可以通过 react-router-dom包引入 Redirect组件进行路由重定向。

Redirect写在注册路由的下方(兜底)—当所有路由都匹配不上时 自动跳转到Redirect设置的路径上。

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

在上方路径匹配后,当在浏览器输入路径 http://localhost:3000,默认路径会变为 http://localhost:3000/home

[6] 路径的模糊匹配与严格匹配

在路由跳转时,我们需要将 Link组件的to属性值 与 Route组件的path属性值进行匹配

  • 严格匹配:只有编写路由路径的link和注册路由的path 一模一样时才能成功显示对应的路由
  • 模糊匹配:会将to属性值以 / 划分 为 home a b, path属性同样以/进行划分为home,然后将path划分值按照顺序和to属性值进行匹配 只要path属性值匹配完毕且全部对应(to属性值存在剩余没有关系)则称为匹配

模糊匹配举例说明

<Link to='/home'></Link> 
<Route path='/home' component={Home}/>

可以成功跳转

 <Link to='/home/a/b'></Link> 
 <Route path='/home' component={Home}/>

可以成功跳转

<Link to='/a/home/a/b'></Link> 
<Route path='/home' component={Home}/> 

不能成功跳转

注意:不要随意开启严格匹配,否则会出现一些问题 比如嵌套路由无法匹配

[7] 嵌套路由

首先明确一点:路由的匹配是按照注册路由的顺序进行的.

举例说明
在App.vue(跟组件)注册了 Home路由和About路由,在Home组件注册了 Sun1和Sun2 两个子路由

  • App.jsx
    import About from '../src/pages/About'
    import Home from '../src/pages/Home'
    import {Link,NavLink, Route,Switch,Redirect} from 'react-router-dom'
    import {Component} from 'react'
      export default class App extends Component {
      render(){
        return (
          <div>
            <div className='left'>
              <Link to='/about'>About</Link> 
              <br />
              <NavLink activeClassName='active' to='/home'>Home</NavLink>
              <br />
              <Link to='/home/sun1'>sun1</Link>
              <br />
              <Link to='/home/sun2'>sun2</Link>
            </div>
            <div className='right'>
              <Switch>
                <Route path='/about' component={About}/>
                <Route path='/home' component={Home}/>
                <Redirect  to='/home' />
              </Switch>
            </div>
          </div>
        )
      }
    }
    
  • Home组件
    import React, { Component } from 'react'
    import {Route,Switch} from 'react-router-dom'
    import Sun1 from './New'
    import Sun2 from './New2'
    export default class Home extends Component {
      render() {
        return (
          <div>
            我是home组件
            <Switch>
              <Route path='/home/sun1' component={Sun1}></Route>
              <Route path='/home/sun2' component={Sun2}></Route>
            </Switch>
          </div>
        )
      }
    }
    
  • 当进入页面的路径为/home/sun2时
  • 渲染过程
    • 在渲染过程中先渲染App.vue, 发现页面有注册路由-> 进行路由匹配
    • 经过模糊匹配 Home组件被匹配(/home) -> 渲染home组件
    • 在渲染过程中,发现Home组件中也进行了路由注册->进行路由匹配
    • 经过模糊匹配 发现与Sun2组件完全对应(/home/sun2), 渲染Sun2组件
  • 通过匹配过程我们也可以看出 注册子路由时要写上父路由的path值 不然最初的匹配就失败!
[8] 路由传参

通过路由向组件传参一共有三种方式

1.search参数
  • 传参
    <Link to='url?参数名=参数值&参数名=参数值'></Link>
    
  • 接收
      this.props.location.search // String
    
  • 举例说明
    <Link to='/home/sun1?name=111&age=18'>sun1</Link>
    
    在子组件接收
    console.log('search参数', this.props.location.search) // search参数 ?name=111&age=18
    
    接收到的是 urlencoded编码字符串,需要借助querystring解析
  • querystring插件使用
    • [1] 下载
      使用npm install querystring -D命令下载
    • [2] 引入
      import qs from 'querystring'
      
    • [3] 解析参数
      console.log('search参数', qs.parse(this.props.location.search.split('?')[1]) ) // search参数 {name: '111', age: '18'}
      
2.state参数
  • 传参
    <Link to={{pathname:url, state:{参数名:参数值}}}></Link>
    
  • 接收
      this.props.location.state // Object
    
  • 举例说明
    <Link to={{pathname:'/home/sun1', state:{name:'111', age:18}}}>sun1</Link>
    
    在子组件接收
    console.log('state', this.props.location.state ) // state {name: '111', age: 18}
    
3.params参数
  • 传参
    <Link to=‘url/参数值/参数值’></Link>
    
  • 声明
      <Route path="/demo/test/:参数名/:参数值" component={Test}/>
    
  • 接收
      this.props.match.params // Object
    
  • 举例说明
    <Link to='/home/sun1/111/18'>sun1</Link>
    
    <Route path='/home/sun1/:name/:age' component={Sun1}></Route>
    
    在子组件接收
    console.log('state', this.props.match.params ) // params {name: '111', age: '18'}
    
编程式导航路由传参
// search参数
this.props.history.push('url?参数名=参数值')

// state参数
this.props.history.push(url,{参数名,参数值})

// params参数
this.props.history.push('/home/message/detail/参数值')

路由配置封装

现在有以下路由组件

  • About组件 — /about
  • Home组件— /home
    • sun1组件 — /home/sun1
    • sun2组件 — /home/sun2
  • [1] 在src文件夹下创建一个router文件夹
  • [2] 在router文件夹下创建一个 routerList文件, 用于集成路由表
    import About from '../pages/About'
    import Home from '../pages/Home'
    import Sun1 from '../pages/Home/New'
    import Sun2 from '../pages/Home/New2'
    const routerList = [
      {
        id:1,
        path:'/about',
        component:About
      },
      {
        id:2,
        path:'/home',
        component:Home,
        routes:[
          {
            id:21,
            path:'/home/sun1',
            component:Sun1
          },
          {
            id:22,
            path:'/home/sun2',
            component:Sun2
          },
        ]
      },
      {
        id:3,
        path:'/home'
      }
    ]
    
    export default routerList
    
  • [3] 在router文件夹下创建一个index.jsx文件,用于创建一个路由注册组件
    import React from 'react';
    import { Switch , Route, Redirect} from 'react-router-dom';
    
    // 组件数据通过props传入
    export default function Router(props){
      const { routes } = props
      return (
        <Switch>
          {
            routes.map(route=>{
              if(route.component){
                {/* 渲染每一个组件,若是存在子组件,将子组件数据传递到组件中(routes) */}
                return <Route path={route.path} render={()=> <route.component routes={route.routes} key={route.id} />} ></Route>
              }else{
                return <Redirect to={route.path} key={route.id}/>
              }
              
            })
          }
        </Switch>
      )
    }
    
  • [4] 使用 路由注册组件 进行组件创建(根组件中)
    import Router from './router'
    import routerList from './router/routerList';
    export default class App extends Component {
    render(){
      return (
        <div>
            {/* <Switch>
              <Route path='/about' component={About}/>
              <Route path='/home' component={Home}/>
              <Redirect  to='/home' />
            </Switch> */}
            <Router routes={routerList}/>
        </div>
      )
    }
    
  • [5] 使用 路由注册组件 进行组件创建(其他组件中)
    import Router from '../../router'
    export default class Home extends Component {
      render() {
        const { routes } = this.props
        return (
          <div>
            我是home组件
            {/* <Switch>
              <Route path='/home/sun1' component={Sun1}></Route>
              <Route path='/home/sun2' component={Sun2}></Route>
            </Switch> */}
            <Router routes={ routes }></Router>
          </div>
        )
      }
    }
    

6版本的react-router-dom一些变化

上述的语法以及案例都是使用的5.2.0版本的,现在我进行了以下操作

  • [1] npm uninstall react-router-dom
  • [2] npm i react-router-dom

现在项目中使用的react-router-dom版本为 6.6.1

然后直接运行,会有一些语法问题 如下:

[1] Switch组件被Routes组件所替代

在这里插入图片描述
在6版本之后将Switch组件移除,新添加了Routes组件
语法

<Routes>
  // 组件注册
</Routes>

两者的异同点为

  • 相同点:
    • 作用都是使得路由仅匹配一个就结束匹配;
  • 区别:
    • 在6之前 组件注册可以被Switch包裹,也可以不使用Switch包裹(不包裹路径就可以匹配多个路由)
    • 在6之后 组件注册必须使用 Routes组件包裹(不包裹就会报错)
[2] Redirect组件被 Navigate 组件所替代

在这里插入图片描述
在6版本之后将Redirect组件移除,新添加了Navigate组件

语法

  <Navigate to='修改的路径' replace='是否使用push'></Navigate>

只要该组件被渲染,就会引起路径修改,切换视图(类似于编程式导航)

  • to:to属性的属性值为切换的路由路径
  • replace:replace的属性值为boolean值 -> 是否使用push
[3] NavLink 属性的变化
  • NavLink组件不再支持 activeClassName属性;
    在这里插入图片描述
  • claaName属性值发生了变化 -> 可以字符串也可以为函数
    • 若是值为字符串 -> 在点击当前路由链接时会为该组件添加一个名为active的类;
    • 若是值为一个回调函数 -> 该函数在初始化时会调用一次且在每一次点击路由链接时都会调用这个函数。
      • 参数:在函数调用时会将是否选中的为当前路由(isActive)作为参数传入,我们可以根据次做判断
      • 返回值:就是类名;
        editclass = (data) =>{
          const { isActive } = data
          return isActive ? 'active' : ''
        }
        <NavLink className={this.editclass} to='/home' end>Home</NavLink>
        
  • 存在end属性
    • false(默认值):只要路径图模糊匹配就相当于选中当前NavLink -> 也就是说选中当前路由的子路由当前路由也会高亮
  • true:只有匹配子当前路由才高亮,匹配子路由不高亮;
[4]添加hook - useRoute

useRout的作用是用于注册路由的(类似于我们之前自己封装的路由配置)

  • 参数是一个路由表
  • 返回值为 多个注册路由组件

需要注意的是 hook 不能在类中使用,若是组件使用类写的,需要转换为function组件。

现在对上面 路由配置封装 环节的代码进行改造

  • [1] routerList.js
    import About from '../pages/About'
    import Home from '../pages/Home'
    import Sun1 from '../pages/Home/New'
    import Sun2 from '../pages/Home/New2'
    import { Navigate } from 'react-router-dom'
    const routerList = [
      {
        id:1,
        path:'/about',
        element:<About />
      },
      {
        id:2,
        path:'/home',
        element:<Home />,
        children:[
          {
            id:21,
            path:'/home/sun1',
            element:<Sun1 />
          },
          {
            id:22,
            path:'/home/sun2',
            element:<Sun2 />
          },
        ]
      },
      {
        id:3,
        path:'/',
        element: <Navigate to='/home' />
      }
    ]
    
    export default routerList
    
    在6版本中 Route组件的component属性被element 属性所替代;
  • 根组件中
    import {Link,NavLink, useRoutes} from 'react-router-dom'
    import './App.css';
    import routerList from './router/routerList';
    export default function App (){
      const routes = useRoutes(routerList)
    
      return (
        <div>
          <div className='left'>
            <Link to='/about'>About</Link> 
            <br />
            <NavLink  to='/home' end>Home</NavLink>
            <br />
            <Link to='/home/sun1'>sun1</Link>
            <br />
            <Link to='/home/sun2'>sun2</Link>
          </div>
          <div className='right'>
            {/* <Switch>
              <Route path='/about' component={About}/>
              <Route path='/home' component={Home}/>
              <Redirect  to='/home' />
            </Switch> */}
            {routes}
          </div>
        </div>
      )
    }
    
    在根组件中将通过 useRoutes 得到的注册路由 放在 路由对应的位置,使得页面中数据可以正常显示,但是若是存在嵌套路由应该如何处理呢?
    若是存在嵌套路由,需要借助 Outlet 组件
Outlet组件

若是存在嵌套路由,需要借助 Outlet 组件

Outlet 组件相当于Vue中的路由出口 ,放在哪里当组件显示时就会将组件显示在对应的位置

import React, { Component } from 'react'
import {Outlet} from 'react-router-dom'
export default class Home extends Component {
 render() {
   return (
     <div>
       我是home组件
       {/* <Switch>
         <Route path='/home/sun1' component={Sun1}></Route>
         <Route path='/home/sun2' component={Sun2}></Route>
       </Switch> */}
       <Outlet />
     </div>
   )
 }
}
路由跳转及传参

若是使用 hook(useRoute) 进行路由注册,则路由跳转的语法也会对应发生变化

  • params传参
    • 传递参数和之前一样

      <Link to='/home/sun1/参数值/参数值'>sun1</Link>
      {
        id:21,
        path:'/home/sun1/:属性名/:属性名',
        element:<Sun1 />
      },
      
      <Link to='/home/sun1/111/18'>sun1</Link>
      {
        id:21,
        path:'/home/sun1/:name/:age',
        element:<Sun1 />
      },
      
    • 接收参数需要借助 hook useParams

      import { useParams } from 'react-router-dom'
      const params = useParams() //{name: '111', age: '18'}
      

      useParams会解析params参数

  • search传参
    • 传递参数与之前相同
        <Link to='/home/sun1?name=111&age=18'>sun1</Link>
      
    • 接收参数需要借助 hook useSearchParams
      const [searchParams, setSearch] = useSearchParams() 
      console.log('res', searchParams.get('name'), searchParams.get('age')) // 111 18
      
      useSearchParams的返回值为一个数组,第一个元素为一个对象,该对象不可直接获取属性,需要通过get方法获取参数
  • state传参
    • 传递与之前有所不同
      // 之前
       <Link to={{pathname:'/home/sun1', state:{name:'111', age:'18'}}}>sun1</Link> 
       // 现在
       <Link to='/home/sun1' state={{name:'111', age:'18'}}>sun1</Link> 
      
    • 获取参数需要借助 hook useLocation
      const {state} = useLocation() 
      console.log('res',state) // {name: '111', age: '18'}
      

路由懒加载

通过以上方式配置的路由,在初始化时会将所有的路由组件同时加载,增加首评的加载速度。

使用懒加载之后就会在跳转到该路由时才会加载该路由

语法

import {lazy} from 'react'
lazy为一个函数,参数为一个回调函数,当跳转到该组件路径时,自自动调用该回调函数

举例说明

  // 普通路由
  // import About from '../pages/About'
  // import Home from '../pages/Home'
  // import StateDemo from '../pages/useSetstate'

  // 路由懒加载
  import {lazy} from 'react'
  import { Navigate } from 'react-router-dom'
  const Home = lazy(()=> import('../pages/Home'))
  const About = lazy(()=> import('../pages/About'))
  const StateDemo = lazy(()=>import('../pages/useSetstate'))
  
  const routerList = [
    {
      id:1,
      path:'/about',
      element:<About />
    },
    {
      id:2,
      path:'/home',
      element:<Home />,
    },
    {
      id:4,
      path:'/demo1',
      element:<StateDemo />
    },
    {
      id:3,
      path:'/',
      element: <Navigate to='/home' />
    }
  ]
  export default routerList

报错->此时会报错
在这里插入图片描述
原因
因为只有跳转到对应路径才会加载对应的组件,这个加载得时间中我们需要展示一个对应的组件,此时可以使用Suspense这个组件

import React,{Suspense} from 'react';
root.render(
  <BrowserRouter>
    <React.StrictMode>
      <Provider store={store}>
        <Suspense fallback={<h1>loading......</h1>}>
          <App/>
        </Suspense>
      </Provider>
    </React.StrictMode>
  </BrowserRouter>
);

fallback属性值可以是一个组件,也可以是一个虚拟dom

<Suspense fallback={<h1>loading......</h1>}>
  <App/>
</Suspense>
<Suspense fallback={<Loading />}>
  <App/>
</Suspense>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值