需求:
1、点击左侧导航选项,展示对应的组件内容
2、点击Home组件的菜单,显示二级路由的组件
3、点击Message下的列表,显示三级路由的组件
4、三级路由的组件同属一个,根据路由传参显示不同内容
9.1 一级路由
概要总结
1、安装react-router-dom
2、标签替换成
3、标签里的component属性替换成element
一、安装react-router-dom最新版
npm i react-router-dom
二、<Switch>标签替换成<Routes>
在路由5的版本中,<Switch>标签的作用是优化路由匹配,从上往下匹配,匹配成功就跳出去不再往下匹配。
在6的版本中,<Switch>标签被替换成<Routes>标签。
注意:标签不是必须使用的,而标签必须使用,否则会报错。
import {Routes, Route} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<Routes>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</Routes>
</div>
三、<Route>标签里的component属性替换成element
component属性是用来指定组件,6的版本替换成element,而且组件的引入方式不一样,component的方式是组件名,element的方式是组件标签。例如:component={About},element={<About>}
import {Routes, Route} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<Routes>
<Route path="/about" element={<About/>} />
<Route path="/home" element={<Home/>} />
</Routes>
</div>
9.2 重定向
概要总结
1、标签替换成标签
2、标签的replace属性
3、标签的caseSensitive属性
一、<Redirect>标签替换成<Navigate>标签
在路由5的版本中,<Redirect>标签的作用是在没有匹配到路由的时候,跳转到指定的路由。
在6的版本中,<Redirect>标签被替换成<Navigate>标签。它在注册路由中,跟普通的路由一致,只是在element属性里使用<Navigate>标签。
import {Routes, Route, Navigate} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<Routes>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/" element={<Navigate to="/about" />} />
</Routes>
</div>
二、<Navigate>标签的replace属性
它有一个replace属性,默认值为false,就是默认为push模式,如果需要replace模式,可以设置为true。
import {Routes, Route, Navigate} from 'react-router-dom'
<div className="panel-body">
{/* 注册路由 */}
<Routes>
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
<Route path="/" element={<Navigate to="/about" replace={true}/>} />
</Routes>
</div>
三、<Route>标签的caseSensitive属性
路由的匹配默认是不区分大小写的,如果在Route标签加上caseSensitive,那就区分了大小写,必须一致才能匹配。
import {Routes, Route, Navigate} from 'react-router-dom'
<div className="list-group">
{/* 路由链接 */}
<NavLink className="list-group-item" to="/about">AboutNavLink>
<NavLink className="list-group-item" to="/home">HomeNavLink>
div>
<div className="panel-body">
{/* 注册路由 */}
<Routes>
<Route path="/ABOUT" caseSensitive component={About} />
<Route path="/home" component={Home} />
<Route path="/" element={<Navigate to="/about" replace={true}/>} />
</Routes>
</div>
9.3 NavLink高亮
概要总结
1、<NavLink>的activeClassName替换成className
一、<NavLink>的activeClassName替换成className
路由5的版本中,<NavLink>的activeClassName用来自定义高亮的class类名。在6的版本中,activeClassName取消掉,使用className的函数形式代替。
在选中的时候,它会返回{isActive: true},非选中的时候,它会返回{isActive: false}
<div className="list-group">
{/* 路由链接 */}
<NavLink className={(a) => {console.log(a)}} to="/about">About</NavLink>
<NavLink className="list-group-item" to="/home">Home</NavLink>
</div>
我们可以通过解构赋值,根据isActive的值来动态加载class类名。
<div className="list-group">
{/* 路由链接 */}
<NavLink className={({isActive}) => isActive ? 'list-group-item atguigu': 'list-group-item'} to="/about">AboutNavLink>
<NavLink className={({isActive}) => isActive ? 'list-group-item atguigu': 'list-group-item'} to="/home">HomeNavLink>
</div>
优化代码,抽取切换className的方法。
function computedClassName({isActive}) {
return isActive ? 'list-group-item atguigu': 'list-group-item'
}
<div className="list-group">
{/* 路由链接 */}
<NavLink className={computedClassName} to="/about">AboutNavLink>
<NavLink className={computedClassName} to="/home">HomeNavLink>
</div>
9.4 useRoutes路由表
概要总结
1、使用useRoutes
2、独立配置路由信息
一、useRoutes路由表
在注册路由的时候,模式是一样的,要配置的就是path和element。我们可以单独配置path和element,然后交给一个hook,由它帮我们生成注册路由的结构,这就是路由表。
1、使用useRoutes
useRoutes传入一个数组,数组每一项是一个对象,对象里对应的就是path和element,根据之前注册路由的写法一致即可。
import {NavLink, Navigate, useRoutes} from 'react-router-dom'
const element = useRoutes([
{path: 'about', element: },
{path: 'home', element: },
{path: '/', element: }
])
<div className="panel-body">
{/* 注册路由 */}
{element}
</div>
2、独立文件配置路由
可以把所有关于路由配置的信息抽取到一个独立的文件,不影响具体的组件逻辑。
routes/index.js:
import About from "../pages/About";
import Home from "../pages/Home";
import {Navigate} from "react-router-dom";
export default [
{path: 'about', element: },
{path: 'home', element: },
{path: '/', element: }
]
App.jsx:
import {NavLink, useRoutes} from 'react-router-dom'
import routes from './routes'
const element = useRoutes(routes)
<div className="panel-body">
{/* 注册路由 */}
{element}
</div>
9.5 嵌套路由
概要总结
1、路由表配置子路由
2、路由占位符
3、子路由的to不带父级路径
4、的end属性
一、路由表配置子路由
在路由表配置子路由跟普通配置子路由一样,也是通过children属性,里面也是对象和path、element属性。
注意:在路由表的子路由,path不需要带上父级路由的路径,连/也不要带,直接写路由名即可。
import {Navigate} from 'react-router-dom';
import About from '../pages/About';
import Home from '../pages/Home';
import Message from '../pages/Message';
import News from '../pages/News';
export default [
{
path: 'about',
element: <About/>
},
{
path: 'home',
element: <Home/>,
children: [
{
path: 'news',
element: <News/>
},
{
path: 'message',
element: <Message/>
}
]
},
{
path: '/',
element: <Navigate to='/about'/>
}
]
二、路由占位符<Outlet/>
在路由5的版本中,路由是定义在组件里,路由定义在哪里,组件就显示在哪里。
而版本6使用了路由表之后,需要在路由组件呈现的位置使用<Outlet/>标签。
import React from 'react';
import {NavLink, Outlet} from 'react-router-dom'
const Home = () => {
return (
<div>
<h2>我是Home的内容</h2>
<ul className="nav nav-tabs">
<li>
<NavLink className="list-group-item" to="/home/news">News</NavLink>
li>
<li>
<NavLink className="list-group-item" to="/home/message">Message</NavLink>
</li>
</ul>
{/* 指定路由组件呈现的位置 */}
<Outlet/>
</div>
);
};
export default Home;
三、子路由的to不带父级路径
在路由5的版本中,<NavLink>的子路由路径需要带上完整的路径。
而版本6,<NavLink>子路由的to路径跟路由表的path一样,不需要带上父级路径,连/也不需要。
import React from 'react';
import {NavLink, Outlet} from 'react-router-dom'
const Home = () => {
return (
<div>
<h2>我是Home的内容</h2>
<ul className="nav nav-tabs">
<li>
<NavLink className="list-group-item" to="news">News</NavLink>
</li>
<li>
<NavLink className="list-group-item" to="message">Message</NavLink>
</li>
</ul>
{/* 指定路由组件呈现的位置 */}
<Outlet/>
</div>
);
};
export default Home;
它不带任何路径和/,其实就相当于相对路径,例如to="news"等同于to="./news"。
四、<NavLink>的end属性
正常来说子路由高亮,父级路由也一起高亮。也可以通过<NavLink>的end属性,只让子路由高亮,父级路由不高亮。
注意:该属性设置在父级路由的标签里。
<div className="list-group">
{/* 路由链接 */}
<NavLink className="list-group-item" to="/about">About</NavLink>
<NavLink className="list-group-item" end to="/home">Home</NavLink>
</div>
9.6 路由的params参数
概要总结
1、路由params参数的传递与声明接收
2、路由组件useParams接参
3、路由组件useMatch接参
一、路由params参数的传递与声明接收
在路由的params参数的传递与声明而言,版本5和6是一致的。
路由表:
export default [
{
path: 'about',
element: <About/>
},
{
path: 'home',
element: <Home/>,
children: [
{
path: 'news',
element: <News/>
},
{
path: 'message',
element: <Message/>,
children: [
{
path: 'detail/:id/:title/:content',
element: <Detail/>
}
]
}
]
},
{
path: '/',
element: <Navigate to='/about'/>
}
]
pages/Message.jsx:
<div>
<ul>
{
messages.map(m => {
return (
// 路由链接
<li key={m.id}>
<Link to={`detail/${m.id}/${m.title}/${m.content}`}>{m.title}</Link>
</li>
)
})
}
</ul>
<hr/>
{/* 指定路由的展示位置 */}
<Outlet/>
</div>
二、路由组件useParams接参
在路由5的版本中,路由参数可以通过this.props里面的history、location、match里获取。
在函数式组件里没有this,需要借助useParams来接参。这个useParams()直接返回一个对象,包含了所有的参数,直接解构赋值就可以取出来。
pages/detail.jsx:
import React from 'react';
import {useParams} from 'react-router-dom'
const Detail = () => {
const {id, title, content} = useParams()
console.log(useParams())
return (
<ul>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul>
);
};
export default Detail;
三、路由组件useMatch接参
除了useParams这个hook,还可以使用useMatch()获取,它必须要传一个注册路由的路径作为参数去匹配。
pages/detail.jsx:
import React from 'react';
import {useParams, useMatch} from 'react-router-dom'
const Detail = () => {
const {id, title, content} = useParams()
console.log(useMatch('/home/message/detail/:id/:title/:content'))
return (
<ul>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul>
);
};
export default Detail;
可以发现,它返回的对象之中,有一个params属性的值就是我们想要的params参数。
这个相比useParams相对复杂一些,没有useParams好用。
9.7 路由的search参数
概要总结
1、路由组件传递search参数
2、useSearchParams钩子
3、useLocation钩子
一、路由组件传递search参数
1、路由链接传递search参数
传递search参数,其实就是把参数通过?和&以键值对的方式拼在路径的后面,类似于ajax的query参数。
import React, {useState} from 'react'
import {Link, Outlet} from 'react-router-dom'
const Message = () => {
const [messages] = useState([
{id: '001', title: '消息1', content: '锄禾日当午'},
{id: '002', title: '消息2', content: '汗滴禾下土'},
{id: '003', title: '消息3', content: '谁知盘中餐'},
{id: '004', title: '消息4', content: '粒粒皆辛苦'}
])
return (
<div>
<ul>
{
messages.map(m => {
return (
// 路由链接
<li key={m.id}>
<Link to={`detail?id=${m.id}&title=${m.title}&`}>{m.title}</Link>
</li>
)
})
}
</ul>
<hr/>
{/* 指定路由的展示位置 */}
<Outlet/>
</div>
);
};
export default Message;
二、使用useSearchParams钩子
1、获取search参数
useSearchParam()返回两个值,一个是search值一个是更新search参数方法,search值通过get的方法传入键来获取对应的值。
import React from 'react';
import {useSearchParams} from "react-router-dom";
const Detail = () => {
const [search, setSearch] = useSearchParams();
const id = search.get('id')
const title = search.get('title')
const content = search.get('content')
return (
<ul>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul>
);
};
export default Detail;
2、更新search参数
更新search参数方法可以通过传入search参数进行改变。
import React from 'react';
import {useSearchParams} from "react-router-dom";
const Detail = () => {
const [search, setSearch] = useSearchParams();
return (
<ul>
<li>
<button onClick={() => setSearch('id=008&title=哈哈&content=嘻嘻')}>点我更新一下收到的search参数</button>
</li> ......
</ul>
);
};
export default Detail;
三、useLocation钩子
在路由5的版本中,search参数是在props里的location属性里。所以在6的版本也有一个叫做useLocation的钩子给我们获取search参数。
import React from 'react';
import {useSearchParams, useLocation} from "react-router-dom";
const Detail = () => {
......
console.log(useLocation())
return (
......
);
};
export default Detail;
9.8 路由的state参数
概要总结
1、路由组件传递state参数
2、通过useLocation钩子获取state参数
一、路由组件传递state参数
在路由5的版本中,传递state参数,<Link>或者<NavLink>的to要传一个对象,对象里的pathname代表路由路径,而state是传参的键值对。
6的版本中,标签有两个属性,一个是to就是路由名的字符串,state参数跟5版本一样传参数对象,不同的是,5的state是在to里面,而6的state跟to是平级的。
import React, {useState} from 'react'
import {Link, Outlet} from 'react-router-dom'
const Message = () => {
const [messages] = useState([
{id: '001', title: '消息1', content: '锄禾日当午'},
{id: '002', title: '消息2', content: '汗滴禾下土'},
{id: '003', title: '消息3', content: '谁知盘中餐'},
{id: '004', title: '消息4', content: '粒粒皆辛苦'}
])
return (
<div>
<ul>
{
messages.map(m => {
return (
// 路由链接
<li key={m.id}>
<Link
to="detail"
state={{
id: m.id,
title: m.title,
content: m.content
}}
>
{m.title}
</Link>
</li>
)
})
}
</ul>
<hr/>
{/* 指定路由的展示位置 */}
<Outlet/>
</div>
);
};
export default Message;
二、通过useLocation钩子获取state参数
在路由5的版本中,可以从props里的location获取state参数。
6的版本中,提供了useLocation钩子来获取。
import React from 'react';
import {useLocation} from 'react-router-dom'
const Detail = () => {
const {state: {id, title, content}} = useLocation()
console.log(useLocation())
return (
<ul>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul>
);
};
export default Detail;
9.9 编程式路由导航
概要总结
1、useNavigate钩子的使用
需求:
1、点击查看详情,可以调转路由并携带参数
2、点击前进后退,可以实现路由的前进和后退
一、useNavigate钩子实现编程式路由导航
编程式路由导航,简单来说就是不借助或者,而是通过代码来实现路由跳转。
路由5的版本是用this.props.history的push和replace两个方法来实现。6的版本使用useNavigate钩子实现。
1、实现路由跳转
使用useNavigate()钩子跳转很简单,直接传路由路径即可。
import React, {useState} from 'react'
import {Link, Outlet, useNavigate} from 'react-router-dom'
const Message = () => {
const [messages] = useState([
{id: '001', title: '消息1', content: '锄禾日当午'},
......
])
const navigate = useNavigate()
function showDetail() {
navigate('/about')
}
return (
<div>
......
<hr/>
{/* 指定路由的展示位置 */}
<Outlet/>
</div>
);
};
export default Message;
2、实现路由传参
useNavigate的第二个参数是用于传参的,它是一个对象,有两个参数:replace可以设置路由模式,boolean类型;state属性就是传参的,把要传的参数通过键值对传过去即可。
const Message = () => {
const [messages] = useState([
{id: '001', title: '消息1', content: '锄禾日当午'},
......
])
const navigate = useNavigate()
function showDetail(m) {
navigate('detail', {
replace: false,
state: {
id: m.id,
title: m.title,
content: m.content
}
})
}
return (
<div>
<ul>
{
messages.map(m => {
return (
// 路由链接
<li key={m.id}>
......
<button onClick={() => showDetail(m)}>查看详情</button>
</li>
)
})
}
</ul>
<hr/>
{/* 指定路由的展示位置 */}
<Outlet/>
</div>
);
};
export default Message;
3、实现前进和后退
在路由5的版本中,history提供了go、goBack和goForward三个方法,用于控制路由的前进和后退。
在6的版本就没有那么多api,还是用这个useNavigate钩子,传一个数字过去即可实现前进后退。
import React from 'react';
import {useNavigate} from 'react-router-dom'
const Header = () => {
const navigate = useNavigate()
function back() {
navigate(-1)
}
function forward() {
navigate(1)
}
return (
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
<button onClick={back}>后退</button>
<button onClick={forward}>前进</button>
</div>
</div>
);
};
export default Header;
注意:对于一般组件,在5的版本它们是不具备路由的方法,除非使用了withRouter。在6的版本,只需要引入useNavigate钩子都可以使用。
9.10 useInRouterContext
概要总结
1、useInRouterContext钩子:判断组件是否在路由环境
一、useInRouterContext钩子
如果组件在<Router>的上下文中呈现,则useInRouterContext钩子返回true,否则返回false。
简单来说,如果组件是在<BrowserRouter>或者<HashRouter>包裹下的,那么所有的子组件全都属于在<Router>的上下文,返回true。否则返回false。
例如:<App/>组件是在<Router>里,<Demo>在<Router>外
// 引入ReactDOM的createRoot
import {createRoot} from 'react-dom/client'
// 引入App组件
import App from './App'
import {BrowserRouter} from 'react-router-dom'
import Demo from './components/Demo'
const containter = document.getElementById('root')
const root = createRoot(containter)
// 渲染App到页面
root.render(
<div>
<Demo/>
<BrowserRouter>
<App/>
</BrowserRouter>
</div>
)
export default root
Demo.jsx:
import React from 'react';
import {useInRouterContext} from 'react-router-dom'
const Demo = () => {
console.log('Demo:' + useInRouterContext()) // Demo:false
return (
<div>Demo</div>
);
};
export default Demo;
App.jsx:
import React from 'react';
import {NavLink, useRoutes, useInRouterContext} from 'react-router-dom'
import routes from './routes'
import Header from './components/Header'
export default function App() {
// 根据路由表生产对应的路由规则
const element = useRoutes(routes)
console.log('App:', useInRouterContext()) // App:true
return (
<div>
......
</div>
);
}
9.11 useNavigationType
概要总结
1、useNavigationType钩子:返回当前的导航类型
一、useNavigationType钩子
1、作用
返回当前的导航类型(用户是如何来到当前页面的)
2、返回
它的返回值有POP、PUSH、REPLACE
3、备注
POP是指在浏览器中直接打开了这个路由组件(刷新页面)
pages/News.jsx
import React from 'react';
import {useNavigationType} from 'react-router-dom'
const News = () => {
console.log(useNavigationType())
return (
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
);
};
export default News;
点击路由跳转:PUSH或者REPLACE
刷新页面:POP
9.12 useOutlet
概要总结
1、useOutlet钩子:返回当前的导航类型
一、useOutlet钩子
1、作用
用来呈现当前组件中渲染的嵌套路由。
2、备注
(1)如果嵌套路由没有挂在,则result为null
(2)如果嵌套路由已经挂在,则暂时嵌套的路由对象
pages/Home.jsx:
import React from 'react';
import {NavLink, Outlet, useOutlet} from 'react-router-dom'
const Home = () => {
console.log('嵌套路由', useOutlet())
return (
<div>
<h2>我是Home的内容</h2>
<ul className="nav nav-tabs">
<li><NavLink className="list-group-item" to="news">News</NavLink></li>
<li><NavLink className="list-group-item" to="message">Message</NavLink></li>
</ul>
{/* 指定路由组件呈现的位置 */}
<Outlet/>
</div>
);
};
export default Home;
嵌套路由没加载:
嵌套路由已加载:
9.13 useResolvedPath
概要总结
1、useResolvedPath钩子:解析路径的path、search、hash值
一、useResolvedPath钩子
1、作用
给定一个URL值,解析其中的path、search、hash值
pages/News.jsx:
import React from 'react';
import {useResolvedPath} from 'react-router-dom'
const News = () => {
console.log('/user?id=001&name=tom#que', useResolvedPath('/user?id=001&name=tom#que'))
return (
<ul>
<li>news001</li>
<li>news002</li>
<li>news003</li>
</ul>
);
};
export default News;