Skip to content

router

react中使用的是 react-router-dom 这个包实现路由功能。

中文文档地址

shell
路由的本质就是一个 key  value 的映射关系
key		--> url
value	--> react路由组件

作用:
当在访问url地址的时候,展示这个地址对应的react路由组件

前端路由模式,是解决SPA应用的手段
S  single 单独
P  page 页面 
A  application 应用
SPA- 单页面应用:只有一个 html的应用

基本概念

Routes组件

Routes组件:路由出口组件,包裹的路由配置将渲染此处

Route组件

Route组件:路由配置

shell
属性:
path:		指定url
element:	指定 路由组件

示例:
<Route path='/login' element={<Login/>}></Route>

使用案例:

jsx
<Routes>
	<Route path='/login' element={<Login/>}></Route>
     <Route path='/home'  element={<Home/>}></Route>
</Routes>
// localhost:3000/login   ===>  Login组件的内容
// localhost:3000/home   ===>  Home组件的内容

BrowserRouter组件

是一个容器组件,只有被该组件包裹的组件及其后代组件,才能进行路由切换

jsx
<BrowserRouter>
	<App/>
</BrowserRouter>

基本使用

安装

shell
npm i react-router-dom

导入容器组件:在入口文件导入:src->index.js

shell
import {BrowserRouter} from 'react-router-dom

使用容器组件包裹根组件:在入口文件配置:src->index.js

jsx
<BrowserRouter>
	<App/>
</BrowserRouter>

创建路由组件:路由组件的存放目录

shell
src/pages or  src/views : 路由组件
src/components: 非路由组件

普通组件和路由组件有什么区别?
路由组件有 key value 的配置

进行路由配置:在路由出口位置配置:src->App.jsx

shell
import { Route, Routes } from 'react-router-dom'

<Routes>
	<Route path='/login' element={<Login/>}></Route>
    <Route path='/home'  element={<Home/>}></Route>
</Routes>
//当对应的路由激活的时候,对应路由组件会替换掉 <Routes>组件所在位置

设置路由重定向

shell
# 写法一:使用Navigate组件包裹
<Route path='/' element={<Navigate to='/home' />}></Route>
# 写法二:使用index={true} 代替 path='/'
<Route index={true} element={<Navigate to='/home' />}></Route>
# 写法三:写法二的一种省略方式
<Route index element={<Navigate to='/home' />}></Route>

设置路由404

shell
# 没有url 匹配成功的时候,会匹配到 * 展示页面找不到的路由组件,写在最后
<Route path='*' element={<PageNotFound/>}></Route>

路由重定向

通过使用 Navigate 组件设置路由重定向

shell
 {/*  Navigate重定向  */}
{/* <Route path='/' element={<Navigate to='/home' />}></Route> */}
{/* <Route index={true} element={<Navigate to='/home' />}></Route> */}
<Route index element={<Navigate to='/home' />}></Route>

路由跳转

NavLink 和 Link组件都会渲染成 a标签,NavLink组件有默认高亮样式 active, Link没有

NavLink组件实现路由跳转

jsx
import { Navigate, NavLink, Route, Routes } from 'react-router-dom'

<h4>NavLink 跳转标签组件 高亮样式 渲染后的a标签上class属性中有 active</h4>
<ul>
    <li><NavLink to="/home">首页</NavLink></li>
    <li><NavLink to="/login">登录页</NavLink></li>
    <li><NavLink to="/user">用户中心</NavLink></li>
</ul>

{/* to='/home/news' 简写为 to='news' */}
<NavLink className="list-group-item" to="news">News</NavLink>

Link组件

Link组件实现路由跳转

jsx
<h4>Link 跳转组件,渲染后的a标签上没有class属性上没有active</h4>
<ul>
    <li><Link to="/home">首页</Link></li>
    <li><Link to="/login">登录页</Link></li>
    <li><Link to="/user">用户中心</Link></li>
</ul>

NavLink组件和Link组件的区别

路由导航自定义类名

NavLink 自定义高亮样式类名

原理:

className 的值可以是一个回调函数,回调函数的参数是一个对象,对象中有一个 isActive的属性,值是布尔值,标识这当前路由是否是激活的。

回调函数的返回值,会成为 className的样式类名

查看这个对象

js
<ul>
	<li><NavLink to="/home" className={(obj) => { console.log(obj)}}>首页</NavLink></li>
	<li><NavLink to="/login">登录页</NavLink></li>
	<li><NavLink to="/user">用户中心</NavLink></li>
</ul>

// 非激活状态
{isActive: false, isPending: false, isTransitioning: false}
// 激活状态
{isActive: true, isPending: false, isTransitioning: false}

使用案例

jsx
<h4>NavLink 自定义高亮样式类名</h4>
<ul>
    <li><NavLink to="/home" className={({isActive}) => {
        return isActive ? 'myselfActive' : ''
    }}>首页</NavLink></li>
    <li><NavLink to="/login" className={({isActive}) => {
        return isActive ? 'myselfActive' : ''
    }}>登录页</NavLink></li>
    <li><NavLink to="/user" className={({isActive}) => {
        return isActive ? 'myselfActive' : ''
    }}>用户中心</NavLink></li>
</ul>

路由表

定义路由配置对象:src->routes->index.js

js
import { Navigate} from 'react-router-dom'
import Home from '../pages/Home'
import Login from '../pages/Login'
import User from '../pages/User'
import NotFound from '../pages/NotFound'

const routes = [
    {
        path: '/login',
        element: <Login />
    },
    {
        path: '/home',
        element: <Home />
    },
    {
        path: '/user',
        element: <User />
    },
    // 重定向
    {
        index: true,   // path:'/'
        element: <Navigate to='/home' />
    },
    // 404配置
    {
        path: '*',
        element: <NotFound />
    }
]

export default routes

useRouters函数

shell
使用 `useRoutes` Hook函数根据路由表生成路由配置
useRoutes(参数) 参数是一个数组对象
useRoutes返回值是一个可用来渲染路由树的有效 React 元素,如果没有匹配项则返回 null

使用案例

在主组件中导入路由对象生成路由映射关系: src->App.js

js
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'

export default function App() {

    return (
        <div>
            <h4>App组件</h4>
            <hr />
            <ul>
                <li><NavLink to="/home" className={({ isActive }) => { return isActive ? 'myActive' : ''} }>首页</NavLink></li>
                <li><NavLink to="/login">登录页</NavLink></li>
                <li><NavLink to="/user">用户中心</NavLink></li>
            </ul>
            <hr />
            {/* 使用useRoutes  hook 根据路由表生成路由配置 */}
            {useRoutes(routes)}
        </div>
    )
}

后代路由

在路由配置对象中通过添加 children 属性配置后代路由

shell
步骤
1. 创建二级路由组件页面
2. 在路由配置对象中通过添加 `children` 属性

示例:
routes=[
    {
        path:'/home',
        children:[
            {
                path:'/home/news',
                element:<News/>
            }
        ]
    }
]

简写:
path:'news',前面不能有 / ,否则会认为是一级路由

Outlet组件

父路由的子路由出口通过 Outlet 组件渲染

js
父路由元素中通过使用 <Outlet> 渲染子路由元素来显示嵌套 UI
如果父路由精确匹配将渲染子索引路由,没有索引路由不渲染任何内容。

使用案例(首页-关于-新闻)

入口文件:src->index.js

js
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>
        <App />
    </BrowserRouter>
    
);

主组件:src->App.jsx

jsx
import React from 'react'
import { NavLink, useRoutes } from 'react-router-dom'
import routes from './routes'

export default function App() {

    return (
        <div>
            <div className="row">
                <div className="col-xs-offset-2 col-xs-8">
                    <div className="page-header">
                        <h2>React Router Demo</h2>
                    </div>
                </div>
            </div>
            <div className="row">
                <div className="col-xs-2 col-xs-offset-2">
                    <div className="list-group">
                        <NavLink className='list-group-item' to='/about'>About</NavLink>
                        <NavLink className='list-group-item' to='/home'>Home</NavLink>
                    </div>
                </div>
                <div className="col-xs-6">
                    <div className="panel">
                        <div className="panel-body">
                            {/* 一级路由,路由组件要展示的位置 */}
                            {useRoutes(routes)}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    )
}

路由配置对象:src->routes->index.js

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

const routes = [
    {
        path:'/home',
        element: <Home />,
        children:[
            {
                // 简写,注意前面不能有 / ,否则就认为是一级路由
                path: 'news',
                element: <News />
            },
            {
                // path:'/home/message', // 完整路径写法
                path: 'message',
                element: <Messages />
            },
            // 重定向
            {
                path: '/home',
                element: <Navigate to='/home/news' />
            }
        ]
    },
    {
        path: '/about',
        element: <About />
    },
    {
        path: '/',
        element: <Navigate to='/home' />
    },
]

export default routes

路由子组件:src->pages->Home->index.jsx

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

export default function Home() {
    return (
        <div>
            <h2>Home组件内容</h2>
            <div>
                <ul className="nav nav-tabs">
                    <li>
                        <NavLink className="list-group-item" to="news">News</NavLink>
                    </li>
                    <li>
                        <NavLink className="list-group-item " to="/home/message">Message</NavLink>
                    </li>
                </ul>
                {/* 二级路由组件展示位置 */}
                <Outlet />
            </div>
        </div>
    )
}

路由子组件:src->pages->About->index.jsx

jsx
import React from 'react'

export default function About() {
  return (
    <div>About</div>
  )
}

路由子组件:src->pages->Home->News->index.jsx

jsx
import React from 'react'

export default function News() {
  return (
    <div>News</div>
  )
}

路由子组件:src->pages->Home->Message->index.jsx

jsx
import React from 'react'

export default function Messages() {
  return (
    <div>Messages</div>
  )
}

编程式导航

useNavigate函数

shell
useNavigate 返回值
返回一个用编程方式导航的函数,可用于例如提交表单。

useNavigate 参数 
1. 传入一个跳转地址'/user' navigate('/user') 
2. 传入第二个可选参数 对象
3. 传想要入 history 堆栈的增量,例如 navigate(-1) 相当于点击后退按钮。

对象中有replace属性,值为布尔值
 replace:true 时,该次路由跳转记录替换history堆栈中最后一次路由记录。
 replace:false 时,该次路由跳转记录新增到history堆栈中最后一次路由记录之后(默认)
jsx
// 默认 新增
<button onClick={ ()=>{navigate('/about')}}>About</button>

// 设置replace属性  替换掉之前的历史记录
<button onClick={()=>{navigate('/about',{replace:true})}}>About</button>

// 设置前进后推
<button onClick={()=>{navigate(1)}}>前进</button>
<button onClick={()=>{navigate(-1)}}>后退</button>
<button onClick={()=>{navigate(2)}}>前进2步</button>

路由传参

path参数

参数通过url路径传递: /user/1/atguigu

基本使用

  1. 在需要路由跳转的地方传递实参:
shell
<Link to='/home/message/path/1/atguigu'>path参数的传递和接收</Link>
  1. 在路由配置对象中设置占位符
js
children:[
 {
     // path-params参数需要配置路径占位符
     path:'/home/message/path/:id/:school',
     element:<PathTest/>
 }
]
  1. 在另一个路由组件中接收数据:使用 useParams() 函数接收
js
// 解构接收path参数
let {id, school} = useParams();

useParams()

shell
useParams 返回值
返回当前 URL <Route path> 匹配的动态参数的键值对对象,对象中包含子路由继承父路由的所有参数。
jsx
import React from 'react'
import { useParams } from 'react-router-dom'

export default function PathTest() {
    let res = useParams();
    // 路由配置中 path:'/home/message/path/:id/:school',
    console.log('res: ', res); // {id: '1', school: 'atguigu'}

    // 解构接收使用
    let { id, school } = useParams();
    return (
        <div>
            <h3>PathTest</h3>
            <p>path id: {id}</p>
            <p>path school: {school}</p>
        </div>
    )
}

query参数

在URL中通过 yuluochenxiao.top?key=value&key2=value2&...形式传递参数

基本使用

  1. 在需要路由跳转的地方传递实参:
shell
<Link to="/home/message/query?username=atguigu&age=20">query参数的传递</Link>
  1. 在路由配置对象中无需多余配置
js
{
    path:'/home/message/query',
    element:<QueryTest/>
}
  1. 在另一个路由组件中接收数据:使用 useSearchParams() 函数接收
js
let [searchQuery, setSearchQuery] = useSearchParams();
let username = searchQuery.get('username')
let age = searchQuery.get('age')

// 设置query参数
setSearchQuery('?username=尚硅谷&age=100')

useSearchParams()

shell
useSearchParams() 用于读取和修改 URL 中当前 location 的查询字符串(query string)


获取数据:
let [searchQuery, setsearchQuery] = useSearchParams()
let username = searchQuery.get('username')

修改query
setSearchQuery('?username=尚硅谷&age=100')
jsx
import React from 'react'
import { useSearchParams } from 'react-router-dom'

export default function QueryTest() {
    let res = useSearchParams();
    console.log('res: ', res) 
    // res是一个数组:[URLSearchParams, ƒ] 如同useState的返回值
    // 但不同的是 URLSearchParams 是一个对象,URLSearchParams对象的原型上URLSearchParams.Prototype存在get方法
    // 调用get方法可以获取到数据
    let [searchQuery, setSearchQuery] = useSearchParams();

    let username = searchQuery.get('username');
    let age = searchQuery.get('age');
    return (
        <div>
            <h3>QueryTest</h3>
            <p>query username: {username}</p>
            <p>query age: {age}</p>
            <p><button onClick={() => {
                setSearchQuery('?username=尚硅谷&age=100')
            }}>设置query参数</button></p>
        </div>
    )
}

state参数

state参数可以传递复杂数据类型,state参数不会出现在地址栏中

基本使用

  1. 在需要路由跳转的地方传递实参:
shell
<Link to='/home/message/state' state={数据}>state参数</Link>
  1. 在路由配置对象中无需多余配置
js
{
    path:'/home/message/state',
    element:<StateTest/>
}
  1. 在另一个路由组件中接收数据:使用 useLocation() 函数接收
js
let { state: { id, title, isDone } } = useLocation();

useLocation()

shell
 hook 返回当前 location 对象,可用于在当前 location 改变时执行一些副作用
jsx
import React from 'react'
import { useLocation } from 'react-router-dom'

export default function StateTest() {
    let x = useLocation();
    console.log('x: ', x);// {pathname: '/home/message/state', search: '', hash: '', state: {…}, key: 'sywlhcu2'}
    // 从state中解构获取数据
    let { state: { id, title, isDone } } = useLocation();
    return (
        <div>
            <h3>StateTest</h3>
            <p>state id: {id}</p>
            <p>state title: {title}</p>
            <p>state isDone: {isDone ? '已完成' : '未完成'}</p>
        </div>
    )
}

路由懒加载

依赖于 react包中的 lazy函数、Suspense组件

基本使用

  1. 在路由配置对象中导入包:src->routes->index.js
shell
import {lazy, Suspense} from 'react'
  1. 使用lazy函数加载路由组件:src->routes->index.js
jsx
const About = lazy(() => import("../pages/About"));
const Message = lazy(() => import("../pages/Message"));
const PathTest = lazy(() => import("../pages/PathTest"));
const QueryTest = lazy(() => import("../pages/QueryTest"));
const StateTest = lazy(() => import("../pages/StateTest"));
  1. 封装Suspense加载组件的函数:src->routes->index.js
jsx
使用 `Suspense组件` 包裹路由组件,设置`fallback`属性,当懒加载的路由组件没有加载完成的时候,显示的内容

function load(Com) {
    return (
        <Suspense fallback={<div>组件正在加载中....</div>}>
            <Com />
        </Suspense>
    )
}
// 调用load函数
element: load(Message)

路由模式

html5 history模式 和 hash路由模式
	BrowserRouter: html5-history模式
	示例:localhost:3000/home/news
	HashRouter: hash路由模式 [了解],使用#锚点,可以解决部分根路径问题 ./ 被解析为根 /
	示例:localhost:3000/#/home/news