React-router 学习笔记

153 阅读7分钟

react-router-dom V6

npm install react-router-dom 安装

BrowserRouter / HashRouter 相当于容器(类似router-view),用于指定路由的模式

BrowserRouter为history模式 HashRouter为hash模式

BrowserRouter组件最好放在最顶层所有组件之外,这样能确保内部组件使用Link做路由跳转时不出错

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/product/:id" element={<ProductDetails/>}></Route>
        <Route path="/home" element={<StudentList />}></Route>
      </Routes>
    </Router>
  );
} 

其中,定义具体的路由

<Route path="/expenses" element={} />

path 为路由名 , element 为对应的组件 . element 的值 必须 写成标签的形式

对比 V5中 老版本V5 中的作用路由

示例:
<BrowserRouter>
 <Route path="/" component={ Home } />
 <Route path="/product" component={ Product } />
</BrowserRouter>


如上代码:

在当用户输入/product时,将会匹配到两个路由,/ 及 /product ;则会显示两个组件 ;

原因是老版本路由在匹配时,是进行模糊匹配

解决方案:

步骤1:使用Switch让路由只能匹配一个; 注意顺序问题,路由先从上向下进行匹配

<BrowserRouter>
 <Switch>
    <Route path="/" element={<Home />} />
    <Route path="/product" element={<Product />} />
 </Switch>
</BrowserRouter>

步骤2:使用exact关键字,让路由进行精准匹配

<Route path="/" exact component={Home} />


加上以上关键字,路由将会精准匹配,只会匹配,path为”/" 的路由
V6中的 组件Routes

v6 中 Switch 名称变为 Routes , 且Route 标签必须包含在Routes标签里,会不然报错

也就是说,路由只能匹配到一个,不会在出现多个路由匹配的情况

v6 中,exact 属性不再需要

v6 内部算法改变,不再需要加exact实现精确匹配路由,默认就是匹配完整路径。

如果需要旧的行为(模糊匹配),路径后加/*

<Route path="/products/*" element={} />

<Route path="/products/:productId" element={} />

测试: /prodcuts 显示

/products/4 显示

/products/haha 显示

/products/haha/hehe 显示

结论:看第6点:React Router 能够自动找出最优匹配路径 ,顺序不重要

若:path属性取值为*时,可以匹配任何(非空)路径,同时该匹配拥有最低的优先级。可以用于设置404页面。

并且v6 中,Route 先后顺序不再重要,React Router 能够自动找出最优匹配路径

v6 保留Link,NavLink

Link,NavLink 类似与a标准,区别NavLink可以设置高亮样式

首页

NavLink的使用,及激活状态的样式设置

V5老版本,activeClassName设置,或activeStyle

首页

V6新版本,activeClassName 与 activeStyle属性被移除

可以直接在的className和style中使用一个函数来设置激活状态的样式。

方法:通过箭头函数接收到isActive参数值,通过isActive的值来设置

通过className

<NavLink

to="/faq"

className={({ isActive }) =>

isActive ? 'active' : ''

}

首页

通过style

<NavLink to="/product/1" style={({ isActive }) => {

return {

fontWeight: "bold",

color: isActive ? "red" : ""

};

}}

fontWeight: "bold" 不管是否激活,都会有; 因为没有判断

  1. Navigate组件

<Route path="/" element ={} />

是对旧的 Redirect 的完整取代。

replace 属性也可以省略,不过行为由 replace 改为 push

replace vs push

        this.props.history.push('router地址')

        push: a-b-c,可以回到上一级

        push跳转会形成history,可返回到上一层。

        this.props.history.replace('router地址')

        replace: a-b-c 回不到上一级 适用于登录后,不需要重新回到登页面

        replace跳转不会形成history,不可返回到上一层。

        结论: push有历史记录,replace没有历史记录

  1. V6中嵌套路由改为相对路径

嵌套路由必须放在 中,且使用相对路径,不再像 v5 那样必须提供完整路径,因此路径变短。

<Route path='/about' element={}>

<Route index element={

} />

<Route path='address' element={

}>

<Route path='information' element={}>

<Route path='joinus' element={}>

上面的访问路径为 /about/address , /about/information, /about/joinus

  1. 使用Outlet组件

此组件是一个占位符,告诉 React Router 嵌套的内容应该放到哪里。

export default class about extends Component {

render() {

return (

关于

  • 公司地址
  • 加入我们
  • 背景故事

)

}

}

  1. 使用index 指定默认路由, 或者path为空

<Route path='/about' element={}>

    <Route index element={

} />

    <Route path='address' element={

}>

    <Route path='information' element={}>

    <Route path='joinus' element={}>

 

或者

设置path为空,来指定默认路由

let router =[{

path: "/home",

element :<Home/ >,

children: [

{

path:"",

element:

},

{

path: " news " ,

element:

}

]

}]

  1. useRoutes 声明式的路由配置方式

声明式路由中,不能写index, 可以让path: "" , 来实现显示默认组件;

useRoutes函数,会返回已经渲染好的路由元素

const GetRoutes=()=>{

return useRoutes([

{

path:'/',

element:

},

{

path:'/home',

element:

},

{

path:'/product',

element:

},

{

path:'/about',

element:,

children:[

{

path:"",

element:

},

{

path:'address',

element:

},

{

path:'join',

element:

},

{

path:'story',

element:

}

]

},

{

path:'/concat',

element:

},

{

path:'/brand',

element:

}

])

}

function App() {

return (

{/*

<Route path='/' element={}>

<Route path='/home' element={}>

<Route path='/product' element={}>

<Route path='/about' element={}>

<Route index element={}>

<Route path='address' element={

}>

<Route path='join' element={}>

<Route path='story' element={}>

<Route path='/concat' element={}>

<Route path='/brand' element={}>

*/}

);

}

export default App;

  1. v6 用useNavigate实现编程式导航,useHistory被移除

import {useNavigate} from "react-router-dom";

const navigate = useNavigate();

//push

navigate("/welcome");

如果要重定向:

navigate("/welcome",{replace:true});

除此之外,还可以使用navigate(-1)后退到前一页,使用navigate(-2)后退到前一页的前一页,navigate(1)前向导航,

注:V5版本中的编程式路由导航 this.props.history.replace() 与  this.props.history.push();

在V6中useNavigate 替代

详细版本:

// v6版本编程导航使用 useNavigate (以下为引入代码)

import { useNavigate } from "react-router-dom";

export default function A() {

const navigate = useNavigate();

//...

}

1.push跳转+携带params参数

navigate(/b/child1/${id}/${title});

2.push跳转+携带search参数

navigate(/b/child2?id=${id}&title=${title});

3.push跳转+携带state参数

navigate("/b/child2", { state: { id, title }});

4.replace跳转+携带params参数

navigate(/b/child1/${id}/${title},{replace: true});

5.replace跳转+携带search参数

navigate(/b/child2?id=${id}&title=${title},{replace: true});

6.replace跳转+携带state参数

navigate("/b/child2", { state: { id, title },replace: true});

  1. useSearch 获取路由参数的方法

在Route组件中的path属性中定义路径参数

在组件内通过useParams hook访问路径参数

import { useParams } from 'react-router-dom';

export default function Foo(){

const params = useParams();

return (

{params.id}

)

}

在以前版本中,组件的props会包含一个match对象,在其中可以取到路径参数。但在最新的6.x版本中,无法从props获取参数。

并且,针对类组件的withRouter高阶组件已被移除。

因此对于类组件来说,使用参数有两种兼容方法:

  1. 将类组件改写为函数组件传递

  2. 写一个HOC来包裹类组件,用useParams获取参数后通过props传入原本的类组件

  3. useSearchParams 获取seach 参数

查询参数不需要在路由中定义

使用useSearchParams hook来访问查询参数。其用法和useState类似,会返回当前对象和更改它的方法

更改searchParams时,必须传入所有的查询参数,否则会覆盖已有参数

import { useSearchParams } from 'react-router-dom';

// 当前路径为 /foo?id=12

function Foo(){

const [searchParams, setSearchParams] = useSearchParams();

console.log(searchParams.get('id')) // 12

setSearchParams({

name: 'foo'

}) // /foo?name=foo

return (

foo

)

}

但在最新的6.x版本中,无法从props获取参数。在类组件中获取seach参数的值,解决方法与上面一样.

  1. useLocation 获取传递的state值

1.传递参数

<NavLink to={detail} state={{

id:item.id,

name:item.name,

content: item.content }}>

{item.name}

navigate("/b/child2", { state: { id, title }});

2.接收参数

import React from 'react'

import { useLocation } from 'react-router-dom'

export default function Detail() {

// 这是连续结构赋值 把useLocation里面呢的state解构,在解构state里面的属性

const {state:{id,name,content}} = useLocation()

return (

  • id:{id}
  • content:{content}
  • name:{name}

)

}

注: prop属性中的location已经没有了,所以在类组件不能获取到相应的数据了,

解决方案就是1. 写成函数 2. 使用高阶组件HOC (13,14,15,16 都是这样)

  1. 多组路由

通常,一个应用中只有一个Routes组件。

但根据实际需要也可以定义多个路由出口(如侧边栏和主页面都要随URL而变化)

  1. 路由组件懒加载

安装: npm i @loadable/component

import loadable from '@loadable/component'

 const ComponentNode = loadable(()=>{

            return import("./"+item.componentPath)

        })

<Route path={item.path} element={}>

  1. 动态路由案例

菜单数据:

var menuInfo = [

{

menuId: 2,

menuName: "用户管理理",

menuUrl: "/index/user",

pathRoute:'user',

pathname: "userlist",

componentPath: "user/UserManger",

menuImgClass: 'TeamOutlined',

pId:0,

menuState: "0",

isContainChildren:false,

menuChilds: [{

menuId: 10,

menuName: "添加用户",

menuUrl: "/index/user/adduser",

pathRoute:'adduser',

pathname: "adduser",

componentPath: "user/AddUser",

menuImgClass: 'VideoCameraAddOutlined',

pId:2,

menuState: "0",

isContainChildren:false,

menuChilds: []

},{

menuId: 11,

menuName: "修改用户",

menuUrl: "/index/user/modifyUser",

pathRoute:'modifyUser',

pathname: "modifyUser",

componentPath: "user/ModifyUser",

menuImgClass: 'VideoCameraAddOutlined',

pId:2,

menuState: "0",

isContainChildren:false,

menuChilds: []

}]

},

{

menuId: 3,

menuName: "角色管理理理",

menuUrl: "/index/role",

pathRoute:'role',

pathname: "role",

componentPath: "user/RoleManger",

menuImgClass: 'WhatsAppOutlined',

pId:0,

menuState: "0",

isContainChildren:true,

menuChilds: [

{

menuId: 7,

menuName: "添加角色",

menuUrl: "/index/role/addrole",

pathRoute:'addrole',

pathname: "addrole",

componentPath: "user/AddRole",

menuImgClass: 'VideoCameraAddOutlined',

pId:3,

menuState: "0",

isContainChildren:false,

menuChilds: []

},

{

menuId: 8,

menuName: "角色详情",

menuUrl: "/index/role/roleInfo",

pathRoute:'roleInfo',

pathname: "roleInfo",

componentPath: "user/RoleInfo",

menuImgClass: 'TagOutlined',

isContainChildren:false,

pId:3,

menuState: "0",

menuChilds: []

},

{

menuId: 9,

menuName: "角色列表",

menuUrl: "/index/role/rolelist",

pathRoute:'rolelist',

pathname: "rolelist",

componentPath: "user/RoleList",

menuImgClass: 'StarOutlined',

pId:3,

menuState: "0",

isContainChildren:false,

menuChilds: []

}

]

}

];

动态路由生成组件:

// 用于创建路由(可以根据数据,生成动态的路由)

import {useRoutes} from 'react-router-dom'

import Login from '../pages/Login'

import Home from '../pages/Home'

// react 动态加载组件 @loadable/component

import loadable from '@loadable/component'

import {observer,inject} from 'mobx-react'

const PrivateRoute = (props)=>{

function bindRouter(list){

let arr = [];

list.map((item)=>{

const ComponentNode = loadable(()=>{

return import("./"+item.componentPath)

})

if(item.menuChilds && item.menuChilds.length>0){

if(item.isContainChildren){

arr.push({

path:item.pathRoute,

element:,

children:[...bindRouter(item.menuChilds)]

})

}else{

arr.push({

path:item.pathRoute,

//element:

children:[...bindRouter(item.menuChilds)]

})

}

}else{

arr.push({

path:item.pathRoute,

element:

})

}

})

return arr;

}

const menuInfo = props.user.userInfo.menuInfo ? props.user.userInfo.menuInfo:[];

return useRoutes([

{

path:"/",

element:

},

{

path:"/index",

element:,

children:[...bindRouter(menuInfo)]

}

])

}

export default inject('user')(observer(PrivateRoute));