React-router v6

100 阅读9分钟

简介

React Router 以三个不同的包发布到 npm 上,它们分别为:

  • react-router :核心模块,包含 React 路由大部分的核心功能,包括路由匹配算法和大部分核心组件和钩子。

  • react-router-dom:React应用中用于路由的软件包,包括react-router的所有内容,并添加了一些特定于 DOM 的 API,包括但不限于BrowserRouter、HashRouter和Link。

  • react-router-native: 用于开发React Native应用,包括react-router的所有内容,并添加了一些特定于 React Native 的 API,包括但不限于NativeRouter和Link。

npm install react-router-dom@6 //安装

在 index.js 中导入路由组件,在 <App> 的最外侧包裹一个 <BrowserRouter> 或 <HashRouter>

  • <BrowserRouter> 表示 history 模式的路由
  • <HashRouter> 表示 Hash 模式的路由
import { createRoot } from "react-dom/client";
import App from './App'
// 这里使用的是 history 模式的路由
import {BrowserRouter} from 'react-router-dom'

// 注意:这里使用的 React18 的写法
const root = createRoot(document.getElementById('root'))
root.render(
  <BrowserRouter>
    <App/>
  </BrowserRouter>
)

常用Api

组件名作用说明
<Routers>一组路由代替原有<Switch>,所有子路由都用基础的Router children来表示
<Router>基础路由Router是可以嵌套的,解决原有V5中严格模式,后面与V5区别会详细介绍
<Link>导航组件在实际页面中跳转使用
<Outlet/>自适应渲染组件根据实际路由url自动选择组件,类似vuerouter中的 <router-view>
<Navigate>导航组件只要该组件被渲染就会切换到对应的路由页面
hooks名作用说明
useParams返回当前参数根据路径读取参数
useNavigate返回当前路由代替原有V5中的 useHistory
useOutlet返回根据路由生成的element
useLocation返回当前的location 对象
useRoutes同Routers组件一样,只不过是在js中使用
useSearchParams用来匹配URL中?后面的搜索参数
useMatch用来匹配URL中?后面的搜索参数
useInRouterContext是否处于路由的上下文环境
useNavigationType返回当前的导航类型
useResolvedPath给定一个 URL值,解析其中的:path、search、hash值

<Routes/> 、<Route/>与<Outlet/>

v6版本中移出了先前的<Switch>,引入了新的替代者:<Routes>

  1. <Routes><Route>要配合使用,且必须要用<Routes>包裹<Route>
  2. <Route> 相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件。
  3. <Route caseSensitive> 属性用于指定:匹配时是否区分大小写(默认为 false)。
  4. 当URL发生变化时,<Routes> 都会查看其所有子 <Route> 元素以找到最佳匹配并呈现组件 。
  5. <Route> 也可以嵌套使用,且可配合useRoutes()配置 “路由表” ,但需要在组件中通过 <Outlet> 组件来渲染其子路由。
 <Routes>
     /*path属性用于定义路径,element属性用于定义当前路径所对应的组件*/
     <Route path="/login" element={<Login />}></Route>
 
 	/*用于定义嵌套路由,home是一级路由,对应的路径/home*/
     <Route path="home" element={<Home />}>
        /*test1 和 test2 是二级路由,对应的路径是/home/test1 或 /home/test2*/
       <Route path="test1" element={<Test/>}></Route>
       <Route path="test2" element={<Test2/>}></Route>
 	</Route>
 	
 	//Route也可以不写element属性, 这时就是用于展示嵌套的路由 .所对应的路径是/users/xxx
     <Route path="users">
        <Route path="xxx" element={<Demo />} />
     </Route>
 </Routes>

嵌套路由

如果在当前的路由是 /messagesOutlet 组件将渲染为 null;如果当前的路由是 /messages/1Outlet 组件将渲染 <MessagesDetails /> 组件

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages" element={<Messages />}>
        <Route path=":id" element={<MessagesDetails />} />
      </Route>
      <Route path="/settings" element={<Settings />} />
    </Routes>
  );
}
import { Outlet } from "react-router-dom";

function Messages() {
  return (
    <div>
      <Conversations />
      <Outlet />
    </div>
  );
}

<Link><NavLink>

修改URL,且不发送网络请求(路由链接),后者有高亮效果

 import { Link } from "react-router-dom";
 
 function Test() {
   return (
     <div>
     	<Link to="/路径">按钮</Link>
     </div>
   );
 }

<Navigate>

只要<Navigate>组件被渲染,就会修改路径,切换视图 replace属性用于控制跳转模式(push 或 replace,默认是push)

 import React,{useState} from 'react'
 import {Navigate} from 'react-router-dom'
 
 export default function Home() {
 	const [sum,setSum] = useState(1)
 	return (
 		<div>
 			<h3>我是Home的内容</h3>
 			{/* 根据sum的值决定是否切换视图 */}
 			{sum === 1 ? <h4>sum的值为{sum}</h4> : <Navigate to="/about" replace={true}/>}
 			<button onClick={()=>setSum(2)}>点我将sum变为2</button>
 		</div>
 	)
 }

useRoutes()

  1. 作用:根据路由表,动态创建<Routes><Route>
  2. 示例代码:
 //路由表配置:src/routes/index.js
 import About from '../pages/About'
 import Home from '../pages/Home'
 import {Navigate} from 'react-router-dom'
 
 export default [
 	{
 		path:'/about',
 		element:<About/>
 	},
 	{
 		path:'/home',
 		element:<Home/>
 	},
 	{
 		path:'/',
 		element:<Navigate to="/about"/>
 	}
 ]
 
 //App.jsx
 import React from 'react'
 import {NavLink,useRoutes} from 'react-router-dom'
 import routes from './routes'
 
 export default function App() {
 	//根据路由表生成对应的路由规则
 	const element = useRoutes(routes)
 	return (
 		<div>
 			......
       {/* 注册路由 */}
       {element}
 		  ......
 		</div>
 	)
 }
 

useNavigate()

  1. 作用:返回一个函数用来实现编程式导航。
  2. 示例代码:
 import React from 'react'
 import {useNavigate} from 'react-router-dom'
 
 export default function Demo() {
   const navigate = useNavigate()
   const handle = () => {
     //第一种使用方式:指定具体的路径
     navigate('/login', {
       replace: false,
       state: {a:1, b:2}
     }) 
     //第二种使用方式:传入数值进行前进或后退,类似于5.x中的 history.go()方法
     navigate(-1)
   }
   
   return (
     <div>
       <button onClick={handle}>按钮</button>
     </div>
   )
 }

useParams()

  1. 作用:回当前匹配路由的params参数,类似于5.x中的match.params
  2. 示例代码:
jsx
 代码解读
复制代码
 import React from 'react';
 import { Routes, Route, useParams } from 'react-router-dom';
 import User from './pages/User.jsx'
 
 function ProfilePage() {
   // 获取URL中携带过来的params参数
   let { id } = useParams();
 }
 
 function App() {
   return (
     <Routes>
       <Route path="users/:id" element={<User />}/>
     </Routes>
   );
 }

useSearchParams()

  1. 作用:用于读取和修改当前位置的 URL 中的查询字符串。
  2. 返回一个包含两个值的数组,内容分别为:当前的seaech参数、更新search的函数。
  3. 示例代码:
 import React from 'react'
 import {useSearchParams} from 'react-router-dom'
 
 export default function Detail() {
 	const [search,setSearch] = useSearchParams()
 	const id = search.get('id')
 	const title = search.get('title')
 	const content = search.get('content')
 	return (
 		<ul>
 			<li>
 				<button onClick={()=>setSearch('id=008&title=哈哈&content=嘻嘻')}>点我更新一下收到的search参数</button>
 			</li>
 			<li>消息编号:{id}</li>
 			<li>消息标题:{title}</li>
 			<li>消息内容:{content}</li>
 		</ul>
 	)
 }
 

useLocation()

  1. 作用:获取当前 location 信息,对标5.x中的路由组件的location属性。
  2. 示例代码:
 import React from 'react'
 import {useLocation} from 'react-router-dom'
 
 export default function Detail() {
 	const x = useLocation()
 	console.log('@',x)
   // x就是location对象: 
 	/*
 		{
       hash: "",
       key: "ah9nv6sz",
       pathname: "/login",
       search: "?name=zs&age=18",
       state: {a: 1, b: 2}
     }
 	*/
 	return (
 		<ul>
 			<li>消息编号:{id}</li>
 			<li>消息标题:{title}</li>
 			<li>消息内容:{content}</li>
 		</ul>
 	)
 }
 
   
 
 

useMatch()

  1. 作用:返回当前匹配信息,对标5.x中的路由组件的match属性。
  2. 示例代码:
 <Route path="/login/:page/:pageSize" element={<Login />}/>
 <NavLink to="/login/1/10">登录</NavLink>
 
 export default function Login() {
   const match = useMatch('/login/:x/:y')
   console.log(match) //输出match对象
   //match对象内容如下:
   /*
   	{
       params: {x: '1', y: '10'}
       pathname: "/LoGin/1/10"  
       pathnameBase: "/LoGin/1/10"
       pattern: {
       	path: '/login/:x/:y', 
       	caseSensitive: false, 
       	end: false
       }
     }
   */
   return (
   	<div>
       <h1>Login</h1>
     </div>
   )
 }

useInRouterContext()

作用:如果组件在 <Router> 的上下文中呈现,则 useInRouterContext 钩子返回 true,否则返回 false。

import React from 'react'
import { useInRouterContext } from 'react-router-dom'

export default function About() {
  // 使用 useInRouterContext() hooks 检查当前组件是否处于路由的上下文环境
  console.log('是否处于路由的上下文环境?', useInRouterContext());
  return (
    <div>
      <h3>我是 About 组件的内容</h3>
    </div>
  )
}

useNavigationType()

  1. 作用:返回当前的导航类型(用户是如何来到当前页面的)。
  2. 返回值:POPPUSHREPLACE
  3. 备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面)。
jsx
 代码解读
复制代码
import React from 'react'
import { useNavigationType } from 'react-router-dom'

export default function About() {
  // 调用 useNavigationType() hooks,可以获取当前路由是以那种跳转模式跳转过来的(PUSH、REPLACE)
  console.log(useNavigationType());
  return (
    <div>
      <h3>我是 About 组件的内容</h3>
    </div>
  )
}

useOutlet()

  1. 作用:用来呈现当前组件中渲染的嵌套路由。
  2. 示例代码:
jsx
 代码解读
复制代码
const result = useOutlet()
console.log(result)
// 如果嵌套路由没有挂载,则result为null
// 如果嵌套路由已经挂载,则展示嵌套的路由对象

useResolvedPath()

  1. 作用:给定一个 URL值,解析其中的:path、search、hash值。
import React from 'react'
import { useResolvedPath } from 'react-router-dom'

export default function News() {
  // 调用 useResolvedPath() hooks,传入 URL 会帮我们解析其中的:path、search、hash值
  console.log(useResolvedPath('/user?id=001&name=coderbin&age=18'));
  return (
    <div>
      <ul>
        <li>news001</li>
        <li>news002</li>
        <li>news003</li>
      </ul>
    </div>
  )
}

路由传参

向路由组件传递 params 参数

步骤:

  1. 传递 params 参数,在路径后面用 / 进行拼接
  2. 在路由表中定义接收路由参数
  3. 对应组件使用 useParams() hooks 接收参数

Message.jsx 传递参数的组件

import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'

export default function Message() {
    const [message] = useState([
    {id: 1, title: 'message1', content: '努力学习'},
    {id: 2, title: 'message2', content: '学习React'},
    {id: 3, title: 'message3', content: '前端工程师'},
  ])
  return (
    <div>
      <ul>
        {
          message.map(m => {
            return (
              <li key={m.id}>
                {/* 1. 传递 params 参数,在路径后面用 / 进行拼接 */}
                <Link to={`detail/${m.id}/${m.title}/${m.content}`}>{m.title}</Link>
              </li>
            )
          })
        }
      </ul>
      <hr />
      {/* 2. 在路由表中定义接收路由参数 path: 'detail/:id/:title/:content' */}
      {/* 3. 路由占位符 */}
      <Outlet/>
    </div>
  )
}

routes.js 路由映射表

import { Navigate } from 'react-router-dom'
import About from '../pages/About'
import Home from '../pages/Home'
import Message from '../pages/Home/Message'
import News from '../pages/Home/News'
import Detail from '../pages/Home/Message/Detail'

// 路由映射表
const routes =  [
  {
    path: '/home',
    element: <Home />,
    children: [
      {path: 'news', element: <News/>},
      {
        path: 'message',
        element: <Message />,
        children: [
          // 声明接收参数
          {path: 'detail/:id/:title/:content', element: <Detail/>}
        ]
      },
      {path: '/home', element: <Navigate to='news'/>}
    ]
  },
  {
    path: '/about',
    element: <About/>
  },
  {
    path: '/',
    element: <Navigate to='/home'/>
  }
]

export default routes

Detail.jsx 接收参数的组件

import React from 'react'
import { useMatch, useParams } from 'react-router-dom'

export default function Detail() {
  // 1. 调用 useParams() hooks,获取传递过来的 params 参数,返回一个参数对象,可以通过解构得出数据
  const { id, title, content } = useParams()

  // 2. 调用 useMatch() hooks,传入完成路径,也可以获取到 params 参数,但是不常用
  const data = useMatch('/home/message/detail/:id/:title/:content')
  console.log(data);
  return (
    <div>
      <ul>
        <li>消息编号:{ id }</li>
        <li>消息标题:{ title }</li>
        <li>消息内容:{content}</li>
      </ul>
    </div>
  )
}

向路由组件传递 search 参数

步骤

  1. 传递 params 参数,在路径后面用 / 进行拼接
  2. 对应组件使用 useSearchParams() hooks 接收参数 Message.jsx 传递参数的组件
import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'

export default function Message() {
    const [message] = useState([
    {id: 1, title: 'message1', content: '努力学习'},
    {id: 2, title: 'message2', content: '学习React'},
    {id: 3, title: 'message3', content: '前端工程师'},
  ])
  return (
    <div>
      <ul>
        {
          message.map(m => {
            return (
              <li key={m.id}>
                {/* 1. 传递 search 参数,在路径后面用 ? 进行拼接 */}
                <Link to={`detail?id=${m.id}&title=${m.title}&content=${m.content}`}>{m.title}</Link>
              </li>
            )
          })
        }
      </ul>
      <hr />
      {/* 2. 不需要在路由表中定义接收路由参数 */}
      {/* 3. 路由占位符 */}
      <Outlet/>
    </div>
  )
}

Detail.jsx 接收参数的组件

import React from 'react'
import { useLocation, useSearchParams } from 'react-router-dom'

export default function Detail() {
  // 1. 调用 useSearchParams() hooks,获取传递过来的 search 参数
  // 通过数组解构的方法,获取 search(URLSearchParams) 对象
  const [search, setSearch] = useSearchParams()

  // 通过 get('search') 方法,获取 search 参数
  const id = search.get('id')
  const title = search.get('title')
  const content = search.get('content')

  // 使用 useLocation() hooks,得到一个location对象,也可以获取 search参数,但是比较麻烦,不常用
  const location = useLocation()
  console.log('location',location);

  return (
    <div>
      <ul>
        {/* 调用 setSearch() 方法,可以改变传递过来的 search */}
        <button onClick={()=>setSearch('id=004&title=测试&content=测试')}>点击更新收到的search参数</button>
        <li>消息编号:{ id }</li>
        <li>消息标题:{ title }</li>
        <li>消息内容:{content}</li>
      </ul>
    </div>
  )
}

向路由组件传递 state 参数

步骤

  1. 传递 params 参数,在路径后面用 / 进行拼接
  2. 对应组件使用 useLocation() hooks 接收参数

Message.jsx 传递参数的组件

import React, { useState } from 'react'
import { Link, Outlet } from 'react-router-dom'

export default function Message() {
    const [message] = useState([
    {id: 1, title: 'message1', content: '努力学习'},
    {id: 2, title: 'message2', content: '学习React'},
    {id: 3, title: 'message3', content: '前端工程师'},
  ])
  return (
    <div>
      <ul>
        {
          message.map(m => {
            return (
              <li key={m.id}>
                {/* 1. 传递 state 参数,添加 state 属性,值为一个对象 */}
                <Link to='detail' state={{id:m.id, title:m.title, content:m.content}}>{m.title}</Link>
              </li>
            )
          })
        }
      </ul>
      <hr />
      {/* 2. 不需要在路由表中定义接收路由参数 */}
      {/* 3. 路由占位符 */}
      <Outlet/>
    </div>
  )
}

Detail.jsx 接收参数的组件

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

export default function Detail() {
  // 调用 useLocation() hooks,通过解构得到一个 state 对象,里面可以拿到传递过来的参数
  // 这里通过连续解构赋值,直接取到参数
  const { state: { id, title, content } } = useLocation()

  return (
    <div>
      <ul>
        <li>消息编号:{id}</li>
        <li>消息标题:{title}</li>
        <li>消息内容:{content}</li>
      </ul>
    </div>
  )
}

路由的编程式导航

使用 useNavigate() hooks 可以实现路由的编程式导航

Message.jsx

import React, { useState } from 'react'
import { Link, Outlet, useNavigate } from 'react-router-dom'

export default function Message() {

  const [message] = useState([
    {id: 1, title: 'message1', content: '努力学习'},
    {id: 2, title: 'message2', content: '学习React'},
    {id: 3, title: 'message3', content: '前端工程师'},
  ])
  
  // 调用 useNavigate() hooks 可以实现编程式导航
  // 接收两个参数:目标路径, 配置对象
  // 配置对象:仅支持 replace 和 state 属性,replace表示跳转的模式,state表示传递的state参数
  const navigate = useNavigate()

  function showDetail(m) {
    navigate('detail', {
      replace: true,
      state: {
        id: m.id,
        title: m.title,
        content: m.content
      }
    })
  }

  return (
    <div>
      <ul>
        {
          message.map(m => {
            return (
              <li key={m.id}>
                {/* 设置路由链接 */}
                <Link to='detail' state={{id:m.id, title:m.title, content:m.content}}>{m.title}</Link>
                {/* 编程式导航 */}
                <button onClick={() => showDetail(m)}>查看详情</button>
              </li>
            )
          })
        }
      </ul>
      <hr />
      {/* 路由占位符 */}
      <Outlet/>
    </div>
  )
}