二十二、React Router 5
大约 7 分钟约 2008 字
(一)React Router 简介
- 将
url
地址和组件进行映射,当用户访问某个地址时与其对应的组件会自动挂载 - 使用步骤
- 引入
react-router-dom
包 - 在
index.js
中引入BrowserRouter
组件 - 将
BrowserRouter
设置为根组件
- 引入
src/index.js
1. import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter as Router } from "react-router-dom";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Router>
<App />
</Router>,
);
src/App.js
2. - 使用
Router
来映射路由地址和组件
1)属性
path
映射的url
地址component
要挂载的组件exact
路径是否开启完整匹配,默认false
Route
的路径被访问时,对应组件就会自动挂载
2)当 - 默认情况下
Route
不是严格匹配,只要url
地址的头部和path
一致,组件就会挂载,不会检查子路径
import { Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
function App() {
return (
<div>
App组件
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
);
}
export default App;
src/components/Home.jsx
3. import React from "react";
const Home = () => {
return (
<div>
<h1>主页有非常好的内容</h1>
</div>
);
};
export default Home;
src/components/About.jsx
4. import React from "react";
const About = () => {
return (
<div>
<h2>关于我们,其实是师徒四人</h2>
<ul>
<li>唐三藏</li>
<li>孙悟空</li>
<li>猪八戒</li>
<li>沙和尚</li>
</ul>
</div>
);
};
export default About;
(二)Link 和 NavLink
src/App.js
1. import { Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Menu from "./components/Menu";
function App() {
return (
<div>
App组件
<Menu />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</div>
);
}
export default App;
src/components/Menu.jsx
2. - 使用
react router
时一定不要使用a
标签创建超链接- 会自动向服务器发送请求并重新加载页面
- 使用
Link
创建超链接 NavLink
和Link
相似,只是可以指定激活样式
import React from "react";
import { Link, NavLink } from "react-router-dom";
import classes from "./Menu.module.css";
const Menu = () => {
return (
<div>
<ul>
<li>
{/* <a href="/">主页</a> */}
{/* <Link to="/">主页</Link> */}
<NavLink
exact
to="/"
// activeClassName={classes.active}
activeStyle={{
textDecoration: "underline",
}}
>
主页
</NavLink>
</li>
<li>
{/* <a href="/about">关于</a> */}
{/* <Link to="/about">关于</Link> */}
<NavLink
exact
to="/about"
// activeClassName={classes.active}
activeStyle={{
textDecoration: "underline",
}}
>
关于
</NavLink>
</li>
</ul>
</div>
);
};
export default Menu;
src/components/Menu.module.css
3. a:link,
a:visited {
color: orange;
text-decoration: none;
}
a:hover,
a.active {
color: red;
text-decoration: underline;
}
(三)两种 Router
Link
链接跳转
1.- 没有经过服务器,可以正常跳转
2.刷新页面或普通链接跳转
- 向服务器发送请求加载数据,此时的请求没有经过
react router
,返回404
3.解决方案
HashRouter
1)使用 - 服务器不会判断
hash
值,请求将由react router
处理 - 地址栏带
#
,对SEO
不友好
index.html
2)修改服务器配置,将所有请求都转发到 location / {
root html;
#index index.html index.htm;
try_files $uri /index.html;
}
HashRouter
- 通过
url
地址中的hash
值来对地址进行匹配
- 通过
BrowserRouter
- 通过
url
地址进行组件的跳转,使用过程和普通url
地址没有区别
- 通过
src/index.js
4. import React from "react";
import ReactDOM from "react-dom/client";
// import { HashRouter as Router } from "react-router-dom";
import { BrowserRouter as Router } from "react-router-dom";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Router>
<App />
</Router>,
);
(四)路由组件
component
1.- 用于指定路由匹配后被挂载的组件
- 需要直接传递组件的类
- 通过
component
构建的组件,会自动创建组件并自动传递参数match
:匹配的信息isExact
:检查路径是否完全匹配params
:请求的参数
location
:地址信息history
:历史记录信息,控制页面跳转push()
:跳转页面replace()
:替换页面
/student/:id
会匹配到 /student/xxx
2.render
3.- 也可以用于指定要挂载的组件
- 需要一个回调函数作为参数,回调函数的返回值最终会被挂载
- 不会自动传递
match
、location
、history
children
4.- 也可以用于指定要挂载的组件,两种用法
- 和
render
类似,传递回调函数【少用】- 该组件无论路径是否匹配都会挂载
- 可以传递组件
src/App.js
5. import { Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Menu from "./components/Menu";
import Student from "./components/Student";
function App() {
return (
<div>
App组件
<Menu />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
{/* <Route path="/student/:id" component={Student} /> */}
{/* <Route
path="/student/:id"
render={(routeProps) => {
console.log(routeProps);
return <Student {...routeProps} />;
}}
/> */}
{/* <Route
path="/student/:id"
children={(routeProps) => {
console.log(routeProps);
return <Student {...routeProps} />;
}}
/> */}
{/* <Route path="/student/:id" children={<Student />} /> */}
<Route path="/student/:id">
<Student />
</Route>
</div>
);
}
export default App;
src/components/About.jsx
6. import React from "react";
const About = (props) => {
console.log(props);
const clickHandler = () => {
// props.history.push({
// pathname: "/student/2",
// });
props.history.replace({
pathname: "/student/2",
});
};
return (
<div>
<button onClick={clickHandler}>点我一下</button>
<h2>关于我们,其实是师徒四人</h2>
<ul>
<li>唐三藏</li>
<li>孙悟空</li>
<li>猪八戒</li>
<li>沙和尚</li>
</ul>
</div>
);
};
export default About;
src/components/Menu.jsx
7. import React from "react";
import { Link } from "react-router-dom";
import "./Menu.module.css";
const Menu = () => {
return (
<div>
<ul>
<li>
<Link to="/">主页</Link>
</li>
<li>
<Link to="/about">关于</Link>
</li>
<li>
<Link to="/student/1">学生</Link>
</li>
</ul>
</div>
);
};
export default Menu;
src/components/Student.jsx
8. import React from "react";
import { useHistory, useLocation, useParams, useRouteMatch } from "react-router-dom";
const STU_DATA = [
{
id: 1,
name: "aaa",
},
{
id: 2,
name: "bbb",
},
{
id: 3,
name: "ccc",
},
{
id: 4,
name: "ddd",
},
];
const Student = (props) => {
// console.log(props.match);
// const stu = STU_DATA.find((item) => item.id === +props.match.params.id);
const stu = STU_DATA.find((item) => item.id === 1);
/*
除了可以通过props获取三个对象外,还可以通过钩子函数获取
*/
const match = useRouteMatch();
const location = useLocation();
const history = useHistory();
const params = useParams();
console.log(match, location, history, params);
return (
<div>
{stu.id} --- {stu.name}
</div>
);
};
export default Student;
(五)路由嵌套
src/App.js
1. import { Route } from "react-router-dom";
import About from "./components/About";
import Hello from "./components/Hello";
import Home from "./components/Home";
import Menu from "./components/Menu";
function App() {
return (
<div>
App组件
<Menu />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
{/* 路由嵌套,也可以定义于About组件中 */}
{/* <Route path="/about">
<About />
<Route path="/about/hello">
<Hello />
</Route>
</Route> */}
</div>
);
}
export default App;
src/components/About.jsx
2. import React from "react";
import { Route, useRouteMatch } from "react-router-dom";
import Hello from "./Hello";
const About = (props) => {
console.log(props);
const { path } = useRouteMatch();
const clickHandler = () => {
// props.history.push({
// pathname: "/student/2",
// });
props.history.replace({
pathname: "/student/2",
});
};
return (
<div>
<button onClick={clickHandler}>点我一下</button>
<h2>关于我们,其实是师徒四人</h2>
<ul>
<li>唐三藏</li>
<li>孙悟空</li>
<li>猪八戒</li>
<li>沙和尚</li>
</ul>
<Route path={`${path}/hello`}>
<Hello />
</Route>
</div>
);
};
export default About;
src/components/Hello.jsx
3. import React from "react";
const Hello = () => {
return <div>Hello</div>;
};
export default Hello;
(六)Switch 组件
src/App.js
1. - 可以将
Route
统一放到一个Switch
中 - 一个
Switch
中只会有一个路由显示
import { Route, Switch } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Menu from "./components/Menu";
function App() {
return (
<div>
App组件
<Menu />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about">
<About />
</Route>
<Route path="*">
<div>路径错误</div>
</Route>
</Switch>
</div>
);
}
export default App;
(七)Prompt 组件
src/App.js
1. import { Route, Switch } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Menu from "./components/Menu";
import MyForm from "./components/MyForm";
function App() {
return (
<div>
App组件
<Menu />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about">
<About />
</Route>
<Route path="/form">
<MyForm />
</Route>
<Route path="*">
<div>路径错误</div>
</Route>
</Switch>
</div>
);
}
export default App;
src/components/MyForm.jsx
2. 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;
(八)Redirect 组件
src/App.js
1. import { useState } from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Menu from "./components/Menu";
import MyForm from "./components/MyForm";
import Login from "./components/Login";
function App() {
const [isLogin, setIsLogin] = useState(false);
return (
<div>
App组件
<Menu />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about">
<About />
</Route>
<Route path={"/login"}>
<Login />
</Route>
<Route path="/form">
{/* <MyForm /> */}
{/* {isLogin ? <MyForm /> : <div>请登录后再操作!</div>} */}
{isLogin ? <MyForm /> : <Redirect to={"/login"} />}
</Route>
<Redirect from={"/abc"} to={"/form"} />
<Route path="*">
<div>路径错误</div>
</Route>
</Switch>
</div>
);
}
export default App;
src/components/About.jsx
2. - 用于跳转页面,替换当前
About
组件 push
属性改为跳转不替换- 也可以定义在
Switch
组件中
import React from "react";
import { Redirect, Route, useRouteMatch } from "react-router-dom";
import Hello from "./Hello";
const About = (props) => {
console.log(props);
const { path } = useRouteMatch();
const clickHandler = () => {
// props.history.push({
// pathname: "/student/2",
// });
props.history.replace({
pathname: "/student/2",
});
};
return (
<div>
{/* <Redirect push to={"/form"} /> */}
<button onClick={clickHandler}>点我一下</button>
<h2>关于我们,其实是师徒四人</h2>
<ul>
<li>唐三藏</li>
<li>孙悟空</li>
<li>猪八戒</li>
<li>沙和尚</li>
</ul>
<Route path={`${path}/hello`}>
<Hello />
</Route>
</div>
);
};
export default About;
src/components/Login.jsx
3. import React from "react";
const Login = () => {
return <div>登录页面</div>;
};
export default Login;