react-router
react-router-v5
react router 可以将url地址和组件进行映射
当用户访问某个地址时,与其对应的组件会自动的挂载
react router 使用步骤
- 引入react-router-dom包
- 在index.js中引入BrowserRouter组件
- 将BrowserRouter设置为根组件
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {BrowserRouter as Router} from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Router>
<App />
</Router>
);
将路由和组件进行映射
使用Route来映射地址和组件
属性:
- path 映射的url地址
- component 要挂载的组件
- exact 路径是否完整匹配,默认值false
当Route的路径被访问,其对应组件就会自动挂载
注意 默认情况下Route并不是严格匹配
只要url地址的头部和path一致,组件就会挂载,不会检查子路径
App.js
import {Route} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
function App() {
return (
<div className="App">
App组件
<Route exact path="/" component={Home}/>
<Route exact path="/about" component={About}/>
</div>
);
}
export default App;
NavLink
在使用react router时,一定不要使用a标签来创建超链接
因为a标签创建的超链接,会自动向服务器发送请求重新加载页面
而我们不希望这种情况发生
可以使用Link组件来创建超链接
NavLink和Link作用相似,只是可以指定链接激活后的样式
Menu.js
import React from 'react';
import {Link, NavLink} from "react-router-dom";
import classes from "./Menu.module.css";
const Menu = () => {
return (
<div>
<ul>
<li>
{/*<Link to="/">主页</Link>*/}
<NavLink
exact
// activeClassName={classes.active}
activeStyle={{textDecoration:"underline"}}
to="/">主页</NavLink>
</li>
<li>
{/*<Link to="/about">关于</Link>*/}
<NavLink
exact
// activeClassName={classes.active}
activeStyle={{textDecoration:"underline"}}
to="/about">关于</NavLink>
</li>
</ul>
</div>
);
};
export default Menu;
HashRouter与BrowserRouter
HashRouter 会通过url地址中的hash值来对地址进行匹配
BrowserRouter 直接通过url地址进行组件的跳转
使用过程中和普通的url地址没有区别
react router 可以将url地址和组件进行映射
当用户访问某个地址时,与其对应的组件会自动的挂载
当我们通过点击Link构建的链接进行跳转时,跳转并没有经过服务器,所以没有问题
但是当我们刷新页面,或通过普通链接进行跳转时,会向服务器发送请加载数据
这时的请求并没有经过react router 所以会返回404
解决方案:
1.使用HashRouter,服务器不会去判断hash值,所以使用HashRouter后请求将会由React Router处理
2.修改服务器的配置,将所有请求都转发到index.html
组件component
component用来指定路由匹配后被挂载的组件
component需要直接传递组件的类
通过component构建的组件,它会自动创建组件并且会自动传递参数
match – 匹配的信息
- isExact 检查路径是否完全匹配
- params 请求的参数
location – 地址信息
history – 控制页面的跳转 - push() 跳转页面
- replace() 替换页面
import {Route} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Menu from "./components/Menu";
import Student from "./components/Student";
function App() {
return (
<div className="App">
<Menu/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
{/*
/student/:id 会匹配到 /student/xxx
*/}
<Route path="/student/:id" component={Student}/>
</div>
);
}
export default App;
student.js
import React from 'react';
const STU_DATA = [
{
id:1,
name:'孙悟空'
},
{
id:2,
name:'猪八戒'
},
{
id:3,
name:'沙和尚'
},
{
id:4,
name:'唐僧'
},
];
const Student = (props) => {
console.log(props);
const stu = STU_DATA.find(item => item.id === +props.match.params.id);
return (
<div>
{stu.id} --- {stu.name}
</div>
);
};
export default Student;
About.js
import React from 'react';
const About = (props) => {
// console.log(props);
const clickHandler = ()=>{
// push()需要一个location作为参数
// props.history.push({
// pathname:'/student/2'
// });
props.history.replace({
pathname:'/student/2',
state:{name:'哈哈'}
});
};
return (
<div>
<button onClick={clickHandler}>点我一下</button>
<h2>关于我们,其实是师徒4人</h2>
<ul>
<li>孙悟空</li>
<li>猪八戒</li>
<li>沙和尚</li>
<li>唐僧</li>
</ul>
</div>
);
};
export default About;
render也可以用来指定要挂载的组件
render需要一个回调函数作为参数,回调函数的返回值会最终被挂载
render不会自动传递三个属性
import {Route} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Menu from "./components/Menu";
import Student from "./components/Student";
function App() {
return (
<div className="App">
<Menu/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/student/:id" render={(routePros)=>{
return <Student {...routePros}/>
}}/>
</div>
);
}
export default App;
children 也可以用来指定被挂载的组件
用法有两种:
- 和render类似,传递回调函数
当children设置一个回调函数时,该组件无论路径是否匹配都会挂载 - 可以传递组件
import {Route} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Menu from "./components/Menu";
import Student from "./components/Student";
function App() {
return (
<div className="App">
<Menu/>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
{/*<Route path="/student/:id" children={(routeProps)=> <Student {...routeProps}/>}/>*/}
{/*<Route path="/student/:id" children={<Student/>}/>*/}
<Route path="/student/:id">
<Student/>*/}
</Route>
{/*<Route path="/student/:id">*/}
{/* {routeProps => <Student {...routeProps}/>}*/}
{/*</Route>*/}
</div>
);
}
export default App;
useRouteMatch() useLocation() useParams()
除了可以通过props获取三个对象外,也可以通过这三个钩子函数来获取
Studet.js
import React from 'react';
import {useHistory, useLocation, useParams, useRouteMatch} from "react-router-dom";
const STU_DATA = [
{
id:1,
name:'孙悟空'
},
{
id:2,
name:'猪八戒'
},
{
id:3,
name:'沙和尚'
},
{
id:4,
name:'唐僧'
},
];
const Student = (props) => {
// console.log(props);
// const stu = STU_DATA.find(item => item.id === +props.match.params.id);
const match = useRouteMatch();
const location = useLocation();
const history = useHistory();
const {id} = useParams();
// const stu = STU_DATA.find(item => item.id === 1);
const stu = STU_DATA.find(item => item.id === +id);
return (
<div>
{stu.id} --- {stu.name}
</div>
);
};
export default Student;
路由嵌套 useRouteMatch()
使用useRouteMatch钩子,可以获取到当前页面路径
About.js
import React from 'react';
import Hello from "./Hello";
import {Route, useRouteMatch} from "react-router-dom";
const About = (props) => {
const {path} = useRouteMatch();
const clickHandler = ()=>{
};
return (
<div>
<button onClick={clickHandler}>点我一下</button>
<h2>关于我们,其实是师徒4人</h2>
<ul>
<li>孙悟空</li>
<li>猪八戒</li>
<li>沙和尚</li>
<li>唐僧</li>
</ul>
<Route path={`${path}/hello`}>
<Hello/>
</Route>
</div>
);
};
export default About;
Switch组件 (已弃用)
可以将Route统一放到一个Switch中,
一个Switch中只会有一个路由显示
import {Route, Switch} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Menu from "./components/Menu";
function App() {
return (
<div className="App">
<Menu/>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about">
<About/>
</Route>
<Route path="*">
<div>路径错误</div>
</Route>
</Switch>
</div>
);
}
export default App;
Prompt
Prompt组件作用是,在用户准备离开该页面时, 弹出提示, 返回true或者false, 如果为true, 则离开页面, 如果为false, 则停留在该页面,
prompt组件里有一个message属性,该属性就是在用户离开页面时, 所提示的文字内容
prompt组件中还有一个when属性, 就是渲染该组件的条件, 应该传入一个布尔值,值为true时,则渲染该组件
import React, {useState} from 'react';
import {Prompt} from "react-router-dom";
const MyForm = () => {
const [isPrompt, setIsPrompt] = useState(false);
return (
<div>
<Prompt
when={isPrompt}
message={"将要离开页面!确认吗?"}/>
<h2>表单</h2>
<input
type="text"
onChange={e => setIsPrompt(e.target.value.trim().length !== 0)}
/>
</div>
);
};
export default MyForm;
prompt组件的message属性,同时还可以传入一个方法来接收要传入的文字, 但是要注意的是, 使用三元运算符操作, 可以向其中直接传入文字, 但是如果使用if else来做判断,那么将无法直接向其中传入文字
<Prompt
message = {() => {
this.state.isOpen? false: "您确定要离开该页面吗?"
}}
/>
if else判断无法向组价中直接传入文字内容, 但是幸好, message能够接收一个方法, 而这个方法, 最终只需要返回一个true或者false就可以, 不管中间你写什么样的代码都无所谓
<Prompt
message = {(location)=>{
if(!isPrompt) {
let leave = window.confirm("您确定要离开该页面吗?")
if(!leave) {
return false
}
}else {
setIsPrompt(false)
return false
}
}}
/>
【总结】:Prompt 有两个属性:message-当用户离开页面时给出的提示信息,when-是否渲染,设置为 true 时,离开页面时总会渲染,设置为 false 时,离开页面时不会渲染。我们就可以利用when设置渲染的时机,当用户对页面内容进行编辑,且未保存时离开,设置when=true,其他情况设置when=false。
Redirect
Redirect 用于跳转页面
import {Redirect, Route, Switch} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Menu from "./components/Menu";
import Student from "./components/Student";
import Hello from "./components/Hello";
import MyForm from "./components/MyForm";
import {useState} from "react";
import Login from "./components/Login";
function App() {
const [isLogin, setIsLogin] = useState(false);
return (
<div className="App">
<Menu/>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about">
<About/>
</Route>
<Route path={"/login"}>
<Login/>
</Route>
<Route path="/form">
{
isLogin ? <MyForm/>:
<Redirect to={"/login"}/>
}
</Route>
{/*<Redirect from={"/abc"} to={"/form"}/>*/}
<Route path="*">
<div>路径错误</div>
</Route>
</Switch>
</div>
);
}
export default App;
react-router-v6
Routes v6 中新增加的组件
作用和Switch类似,都是用于Route的容器
Routes中Route只有一个会被匹配
v6中,Route的component render children都变了
需要通过element来指定要挂载的组件
App.js
import React from 'react';
import {Route, Routes} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Menu from "./components/Menu";
import Student from "./components/Student";
const App = () => {
return (
<div>
<h1>App</h1>
<Menu/>
<Routes>
<Route path="/" element={<Home/>}></Route>
<Route path="about" element={<About/>}></Route>
<Route path="student/:id" element={<Student/>}/>
</Routes>
</div>
);
};
参数和钩子
useParams()来获取参数
useLocation()获取当前的地址信息
useMatch()用来检查当前url是否匹配某个路由
useNavigate()获取一个用于条件页面的函数
import React from 'react';
import {useLocation, useParams, useMatch, useNavigate} from "react-router-dom";
const STU_DATA = [
{
id:1,
name:'刘备'
},
{
id:2,
name:'关羽'
},
{
id:3,
name:'沙和尚'
},
{
id:4,
name:'唐僧'
},
];
const Student = () => {
// 可以使用useParams()来获取参数
const {id} = useParams();
const location = useLocation();// 获取当前的地址信息
// 如果路径匹配,则返回一个对象,不匹配则返回null
//const match = useMatch("/student/:id");// 用来检查当前url是否匹配某个路由
// useNavigate获取一个用于条件页面的函数
const nav = useNavigate();
const stu = STU_DATA.find(item => item.id === +id);
const clickHandler = () =>{
// nav('/about'); // 使用push,会产生历史记录
nav('/about', {replace: true}); // 使用replace 不会产生新的记录
};
return (
<div>
<button onClick={clickHandler}>点我一下</button>
<h2>{stu.id} --- {stu.name}</h2>
</div>
);
};
export default Student;
嵌套路由和Outlet
Outlet 用来表示嵌套路由中的组件
当嵌套路由中的路径匹配成功了,Outlet则表示嵌套路由中的组件
当嵌套路由中的路径没有匹配成功,Outlet就什么都不是
App.js
import React from 'react';
import {Route, Routes} from "react-router-dom";
import Home from "./components/Home";
import About from "./components/About";
import Student from "./components/Student";
import Hello from "./components/Hello";
import Abc from "./components/Abc";
const App = () => {
return (
<div>
<h1>App</h1>
<Routes>
<Route path="/" element={<Home/>}></Route>
<Route path="about" element={<About/>}>
<Route path="hello" element={<Hello/>}></Route>
<Route path="abc" element={<Abc/>}></Route>
</Route>
</Routes>
</div>
);
};
export default App;
About.js
import React from 'react';
import {Outlet} from "react-router-dom";
const About = () => {
return (
<div>
<Outlet/>
</div>
);
};
export default About;
Navigate组件
Navigate 组件用来跳转到指定的位置
默认使用push跳转
About.js
import React from 'react';
import {Outlet, Navigate} from "react-router-dom";
const About = () => {
return (
<div>
<Navigate to="/student/1" replace/>
<Outlet/>
</div>
);
};
export default About;
NavLink
import React from 'react';
import {Link, NavLink} from "react-router-dom";
const Menu = () => {
return (
<div>
<ul>
<li>
<Link to="/">主页</Link>
</li>
<li>
<Link to="/about">关于</Link>
</li>
<li>
<NavLink
style={
({isActive})=>{
return isActive?
{backgroundColor:"yellow"}:
null
}
}
to="/student/2">学生</NavLink>
</li>
</ul>
</div>
);
};
export default Menu;