react-router-dom 配置路由
参考:
- https://www.jianshu.com/p/a0427a55ae47
- https://github.com/remix-run/react-router/blob/main/docs/getting-started/tutorial.md
- https://serializedowen.github.io/docs/react-router-dom/向导/核心组件
- https://reactrouterdotcom.fly.dev/docs/en/v6/getting-started/overview
- https://www.jianshu.com/p/d991a4a55ae1
- https://www.cnblogs.com/menggirl23/p/10288477.html
0 安装
npm install --save react-router
或者
npm install --save react-router-dom
对比
react-router | react-router-dom | |
---|---|---|
关系 | 不能通过操作dom控制路由 | 在React-router的基础上扩展了可操作dom的api |
区别 | 包内容较多 | 比较轻巧 |
跳转方式对比 | 3.0以上版本用this.props.router.push('/path')实现跳转,4.0以上版本用this.props.history.push('/path') | this.props.history.push('/path') |
PS
react-router优点
与React融为一体,专为react量身打造,编码风格与react保持一致,例如路由的配置可以通过component来实现
不需要手工维护路由state,使代码变得简单
强大的路由管理机制,体现在如下方面
路由配置: 可以通过组件、配置对象来进行路由的配置
路由切换: 可以通过 Redirect进行路由的切换
路由加载: 可以同步记载,也可以异步加载,这样就可以实现按需加载
使用方式: 不仅可以在浏览器端的使用,而且可以在服务器端的使用
PS:安装react-router-dom的时候,不需要npm安装react-router。
1 react-router-dom
1.0 v5 升级至 v6 的变化
移除的:
- 把 Switch 标签替换成了 Routes 标签
- component 替换成了 element
- 移除了 Redirect
1.0.0 Switch 不再使用,转而使用更强大的 Routes:
- 可以使用相对路径(path开头不是
/
就是相对路由)
- 根据最佳匹配选择路由,而不是根据路由的排序。
- 路由可以嵌套
PS:
<Routes>
会遍历它的children,生成一个数组。
useRoutes(routesGoHere)
可以实现同样的效果
1.0.1 路径匹配更加精确
<Route path="teams/:teamId" element={<Team />} />
<Route path="teams/new" element={<NewTeamForm />} />
URL /teams/new
都能匹配这两个路由,但是teams/new
更加具体,所以会渲染
1.0.2 useNavigate 实现自定义
import { useNavigate } from "react-router-dom";
function Invoices() {
let navigate = useNavigate();
return (
<div>
<NewInvoiceForm
onSubmit={async (event) => {
let newInvoice = await createInvoice(
event.target
);
navigate(`/invoices/${newInvoice.id}`);
}}
/>
</div>
);
}
1.0.3 useParams()读取路由参数
import { Routes, Route, useParams } from "react-router-dom";
function App() {
return (
<Routes>
<Route
path="invoices/:invoiceId"
element={<Invoice />}
/>
</Routes>
);
}
function Invoice() {
let params = useParams();
return <h1>Invoice {params.invoiceId}</h1>;
}
1.0.4 嵌套式路由
1.0.5 默认子路由
使用场景:有时候url和某个parent路由匹配了,但是没有和这个parent路由的任何子路由匹配,那么这时候就不会渲染任何子组件。
但有时候我们的业务场景是需要默认显示某个子页面的。
解决方法:增加一个含有index属性的子路由,当发生上面的情况的时候自动显示index对应的组件
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Activity />} /> //增加这一行
<Route path="invoices" element={<Invoices />} />
<Route path="activity" element={<Activity />} />
</Route>
</Routes>
);
}
1.0.6 Relative Links
有时候修改了parent路由的名字,就要对应地修改一大堆路由的名字,包括跳转的时候的url,有了relative link就减少了修改。
<Route path="dashboard" element={<Dashboard />}>
<Route path="invoices" element={<Invoices />} />
<Route path="team" element={<Team />} />
</Route>
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<nav>
<Link to="invoices">Invoices</Link>{" "}
<Link to="team">Team</Link>
</nav>
<hr />
<Outlet />
</div>
);
}
1.0.7 在子组件中也能使用Routes
要记得在parent路由里加上'/*'
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard/*" element={<Dashboard />} />
</Routes>
);
}
function Dashboard() {
return (
<div>
<p>Look, more routes!</p>
<Routes>
<Route path="/" element={<DashboardGraphs />} />
<Route path="invoices" element={<InvoiceList />} />
</Routes>
</div>
);
}
1.0.8 Layout路由
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
1.1 名词解释
- Location :基于window.location对象,用来表示“where the user is at”
{
pathname: "/bbq/pig-pickins",
search: "?campaign=instagram",
hash: "#menu",
state: null,
key: "aefz24ie"
}
前三个属性和window.location的属性一样,后两个是React Router独有的。
pathname属性:URL里origin以后的部分(origin包括协议、域名和端口), 比如
https://example.com/teams/hotspurs
的 pathname 是/teams/hostspurs
.
- History: 一个对象。它让React Router修改url,提供API来修改浏览器的 history stack
- 传统网站:用户每次点击前进后退按钮,点击链接,都要向服务器发送请求
- client side routing:
<a
href="/contact"
onClick={(event) => {
// stop the browser from changing the URL and requesting the new document
event.preventDefault();
// push an entry into the browser history stack and change the URL
window.history.pushState({}, undefined, "/contact");
}}
/>
这样就可以阻止浏览器向服务器发送请求。
- 如果想要让UI在URL变化的时候随之改变的话,就要监控URL变化。
但是window.addEventListener("popstate", () => {});
只能监控到history.back()
或者history.forward()
。
如果调用history.pushState()或history.replaceState()不会被监听到。 - React Router 在 push, pop, or replace的时候,项目就会渲染出对应的UI。
- History Action :push/pop/replace
1.1 路由器
- BrowserRouter 使用 HTML5 提供的 history API ,保证你的 UI 界面和 URL 保持同步。
创造一个 history, puts the initial location in to state, and subscribes to the URL.
当history
对象修改了url,就会告知=》 BrowserRouter重新渲染
- HashRouter 使用 URL 的 hash ,保证你的 UI 界面和 URL 保持同步
PS:hash history 不支持 location.key 或 location.state。(https://serializedowen.github.io/docs/react-router-dom/API/hash-router)
1.2 导航(navigation)
有两种方法:
<Link>
useNavigate
1.2.0 Link
React Router will prevent the browser's default behavior and tell the history to push a new entry into the history stack. The location changes and the new matches will render.
1.2.1 useNavigate Hook
let navigate = useNavigate();
useEffect(() => {
setTimeout(() => {
navigate("/logout");
}, 30000);
}, []);
1.3 获取数据
let location = useLocation();
let urlParams = useParams();
let [urlSearchParams] = useSearchParams();
1.4 useSearchParams
let [searchParams, setSearchParams] = useSearchParams();
- setSearchParams({filter:123})会把
?filter=123
插到url后面 - 通过
<Link to="/shoes?brand=nike">Nike</Link>
跳转之后,可以通过searchParams获取search的值,其有下面这些属性:
const handleSearch = () => {
navigate("/user/2333?type=1");
};
import { useParams, useSearchParams } from "react-router-dom";
const UserInfo = (props) => {
const params = useParams();
const [search] = useSearchParams();
console.log(search.get("type")); //打印结果为1
return (
<div>
User Info
<div>The user's index is {params.id}</div>
</div>
);
};
export default UserInfo;
2 举例
/src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import reportWebVitals from "./reportWebVitals";
import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import LayoutPage from "./LayoutPage";
import Users from "./pages/Users";
import UserInfo from "./pages/UserInfo";
import NotFound from "./pages/NotFound";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route element={<LayoutPage></LayoutPage>}>
<Route path="/" element={<Navigate to="/user" />}></Route>
<Route path="/user" element={<Users></Users>}></Route>
<Route path="/user/:id" element={<UserInfo></UserInfo>}></Route>
<Route path="*" element={<NotFound></NotFound>}></Route>
</Route>
</Routes>
</BrowserRouter>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
/src/LayoutPage.js
import { Link, Outlet } from "react-router-dom";
const LayoutPage = (props) => {
return (
<div style={{ height: "100vh", backgroundColor: "gray", display: "flex" }}>
{/* 左边一列 */}
<div
style={{
backgroundColor: "black",
width: "200px",
height: "100%",
display: "flex",
flexDirection: "column",
}}
>
<div style={{ color: "white", height: "48px" }}>Navigation</div>
<div style={{ backgroundColor: "gray", flex: "1 auto" }}>
<Link to="user">to user</Link>
</div>
</div>
{/* 右边一列 */}
<div
style={{
backgroundColor: "white",
flex: "1 auto",
display: "flex",
flexDirection: "column",
}}
>
<div
style={{ height: "48px", backgroundColor: "brown", color: "white" }}
>
HEADER
</div>
<div style={{ flex: "1 auto" }}>
<Outlet></Outlet>
</div>
</div>
</div>
);
};
export default LayoutPage;
src/pages/Users.js
import { Link, useNavigate } from "react-router-dom";
const User = (props) => {
const navigate = useNavigate();
const handleClick = () => {
setTimeout(() => {
navigate(`123`);
}, 3000);
};
return (
<div
style={{
border: "1px solid black",
display: "flex",
flexDirection: "column",
}}
>
User
{[21, 22].map((num) => {
return (
<Link key={num} to={`${num}`}>
User {num}
</Link>
);
})}
<button onClick={handleClick}>点击按钮3秒之后跳转</button>
</div>
);
};
export default User;