二十二、React Router 5
大约 7 分钟约 2008 字
(一)React Router 简介
- 将
url地址和组件进行映射,当用户访问某个地址时与其对应的组件会自动挂载 - 使用步骤
- 引入
react-router-dom包 - 在
index.js中引入BrowserRouter组件 - 将
BrowserRouter设置为根组件
- 引入
1. src/index.js
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>,
);
2. src/App.js
- 使用
Router来映射路由地址和组件
1)属性
path映射的url地址component要挂载的组件exact路径是否开启完整匹配,默认false
2)当 Route 的路径被访问时,对应组件就会自动挂载
- 默认情况下
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;
3. src/components/Home.jsx
import React from "react";
const Home = () => {
return (
<div>
<h1>主页有非常好的内容</h1>
</div>
);
};
export default Home;
4. src/components/About.jsx
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
1. src/App.js
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;
2. src/components/Menu.jsx
- 使用
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;
3. src/components/Menu.module.css
a:link,
a:visited {
color: orange;
text-decoration: none;
}
a:hover,
a.active {
color: red;
text-decoration: underline;
}
(三)两种 Router
1.Link 链接跳转
- 没有经过服务器,可以正常跳转
2.刷新页面或普通链接跳转
- 向服务器发送请求加载数据,此时的请求没有经过
react router,返回404
3.解决方案
1)使用 HashRouter
- 服务器不会判断
hash值,请求将由react router处理 - 地址栏带
#,对SEO不友好
2)修改服务器配置,将所有请求都转发到 index.html
location / {
root html;
#index index.html index.htm;
try_files $uri /index.html;
}
HashRouter- 通过
url地址中的hash值来对地址进行匹配
- 通过
BrowserRouter- 通过
url地址进行组件的跳转,使用过程和普通url地址没有区别
- 通过
4. src/index.js
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>,
);
(四)路由组件
1.component
- 用于指定路由匹配后被挂载的组件
- 需要直接传递组件的类
- 通过
component构建的组件,会自动创建组件并自动传递参数match:匹配的信息isExact:检查路径是否完全匹配params:请求的参数
location:地址信息history:历史记录信息,控制页面跳转push():跳转页面replace():替换页面
2./student/:id 会匹配到 /student/xxx
3.render
- 也可以用于指定要挂载的组件
- 需要一个回调函数作为参数,回调函数的返回值最终会被挂载
- 不会自动传递
match、location、history
4.children
- 也可以用于指定要挂载的组件,两种用法
- 和
render类似,传递回调函数【少用】- 该组件无论路径是否匹配都会挂载
- 可以传递组件
5. src/App.js
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;
6. src/components/About.jsx
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;
7. src/components/Menu.jsx
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;
8. src/components/Student.jsx
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;
(五)路由嵌套
1. src/App.js
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;
2. src/components/About.jsx
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;
3. src/components/Hello.jsx
import React from "react";
const Hello = () => {
return <div>Hello</div>;
};
export default Hello;
(六)Switch 组件
1. src/App.js
- 可以将
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 组件
1. src/App.js
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;
2. src/components/MyForm.jsx
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 组件
1. src/App.js
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;
2. src/components/About.jsx
- 用于跳转页面,替换当前
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;
3. src/components/Login.jsx
import React from "react";
const Login = () => {
return <div>登录页面</div>;
};
export default Login;
