跟着尚硅谷的天禹老师学习React
看视频可以直接点击 b站视频地址
概述
在2021年11月,React官方将ReactRouter6调整为ReactRouter的默认版本
1.React Router以3个不同的包发布到npm上,它们分别为:
- react-router:路由的核心库,提供了很多的组件和钩子。
- react-router-dom:包含了react-router所有内容,并添加了一些专门用于DOM的组件,例如<BrowserRoute/r>
- react-router-native:包括react-router所有内容,并添加了一些专门用于ReactNative的API,例如<NativeRouter/>
2、与React Router 5.x版本,改变了:
- 内置组件的变化:移除了<Swtich>,新增了<Routes>
- 语法的变化:component = {About},变为了 element = {<About />}等
- 新增了多个hook:useParams、useNavigate、useMatch等
- 官方明确推荐函数式组件
HashRouter BrowserRouter
和React Router5.x一样
Routes和Route
- V6版本移除了先前的<Switch/>,引入了新的替代者<Routes>。
- <Routes>和<Route>要配合使用,且必须要用 <Routes>包裹<Route>
- <Route/>相当于一个if域据,如果其路径和当前URL匹配,则呈现其对应的组件。
- 属性用于指定:匹配时是否区分大小写(默认为false)
- 当URL发生变化时,<Routes>都会直接查看其所有子<Route>元素以找到最佳匹配并呈现组件。
- <Route>也可以嵌套使用,且可配合useRoutes()配置“路由表”,但需要通过<Outlet>组件来渲染其子路由。
Link
和React Router5.x一样
NavLink高亮
现在className可以使用回调函数来写,回调接收一个对象作为参数,里面会有一个名为isActive的属性来标识当前链接是否处于被选中状态,函数需要返回一个style。
Navigate
- 作用:只要<Navigate>组件被渲染,就会修改路径,切换视图。
- replace属性用于控制跳转模式(push或replace,默认replace为false)
useRoutes
1、定义一个路由表
const element = useRoutes([
{
path: "/about",
element: <About />,
},
{
path: "/home",
element: <Home />,
},
{
path: "/",
element: <Navigate to="/about" />,
},
]);
2、直接插进jsx中
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{element}
</div>
</div>
</div>
嵌套路由
Outlet
和Vue的路由导入很相似,Vue使用的是<router-view>
目录结构
代码
App.js
/* App.js */
import React, { Component } from "react";
import { NavLink, useRoutes } from "react-router-dom";
import routesMap from "./routes";
export default function App() {
function computeClass({ isActive }) {
return isActive ? "list-group-item highlight" : "list-group-item";
}
const element = useRoutes(routesMap);
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 在React中靠路由链接实现切换组件 */}
<NavLink className={computeClass} to="/about">
About
</NavLink>
{/* 如果希望匹配到子路由时当前链接不高亮,可以加上end属性 */}
<NavLink className={computeClass} end to="/home">
Home
</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
{/* <Routes>
<Route path="/about" element={<About />} />
<Route path="/home" element={<Home />} />
<Route path="/" element={<Navigate to="about" />}></Route>
</Routes> */}
{/* 更简单的写法
优化:可以在外部写一个路由表然后再引入 */}
{element}
</div>
</div>
</div>
</div>
</div>
);
}
路由表 routes/index.js
/* routes/index.js */
import Home from "../pages/Home";
import About from "../pages/About";
import Message from "../pages/Message";
import News from "../pages/News";
import { Navigate } from "react-router-dom";
export default [
{
path: "/about",
element: <About />,
},
{
path: "/home",
element: <Home />,
children: [
{ path: "message", element: <Message /> },
{ path: "news", element: <News /> },
],
},
{
path: "/",
element: <Navigate to="/about" />,
},
];
Home.jsx
/* Home.jsx */
import React from "react";
import { NavLink, Outlet } from "react-router-dom";
export default function Home() {
return (
<div>
<h1>Home</h1>
<div>
<ul className="nav nav-tabs">
<li>
<NavLink className="list-group-item" to="message">
News
</NavLink>
</li>
<li>
<NavLink className="list-group-item" to="news">
News
</NavLink>
</li>
</ul>
{/* 指定路由组件呈现的位置 */}
<Outlet />
</div>
</div>
);
}
路由传参(useParams、useMatch、useSearchParams、useLocation)
和React Router 5.x差不多,有params、match、search、state传参。
对应API:useParams、useMatch、useSearchParams、useLocation(从中可以取state)。
对应传参方式:在路径中显式写上占位符(params和match)、拼接search参数(有点像后端的query)、在Link/NavLink中声明state属性。
App.jsx
import React from "react";
import { NavLink, useRoutes } from "react-router-dom";
import routesMap from "./routes";
export default function App() {
function computeClass({ isActive }) {
return isActive ? "list-group-item highlight" : "list-group-item";
}
const element = useRoutes(routesMap);
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 在React中靠路由链接实现切换组件 */}
<NavLink className={computeClass} to="/about">
About
</NavLink>
{/* 如果希望匹配到子路由时当前链接不高亮,可以加上end属性 */}
<NavLink className={computeClass} end to="/home">
Home
</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
{/* <Routes>
<Route path="/about" element={<About />} />
<Route path="/home" element={<Home />} />
<Route path="/" element={<Navigate to="about" />}></Route>
</Routes> */}
{/* 更简单的写法
优化:可以在外部写一个路由表然后再引入 */}
{element}
</div>
</div>
</div>
</div>
</div>
);
}
routes/index.js
/* routes/index.js */
import Home from "../pages/Home";
import About from "../pages/About";
import Message from "../pages/Message";
import News from "../pages/News";
import Detail from "../pages/Detail";
import { Navigate } from "react-router-dom";
export default [
{
path: "/about",
element: <About />,
},
{
path: "/home",
element: <Home />,
children: [
{
path: "message",
element: <Message />,
children: [
{
// 传递params和match参数时
// path:'detail/:id/:title/:content',
path: "detail",
element: <Detail />,
},
],
},
{ path: "news", element: <News /> },
],
},
{
path: "/",
element: <Navigate to="/about" />,
},
];
Message.jsx
/* Message.jsx */
import React, { useState } from "react";
import { Link, Outlet } from "react-router-dom";
export default function Message() {
const [messages] = useState([
{ id: 1, title: "消息1", content: "内容1" },
{ id: 2, title: "消息2", content: "内容2" },
{ id: 3, title: "消息3", content: "内容3" },
]);
return (
<div>
<ul>
{messages.map((message) => {
return (
// 路由链接
<li key={message.id}>
<Link
// 使用params/match传参
// to={`detail/${message.id}/${message.title}/${message.content}`}
// 使用search传参
// to={`detail?id=${message.id}&title=${message.title}&content=${message.content}`}
// 使用state传参
to="detail"
state={{
id: message.id,
title: message.title,
content: message.content,
}}
>
{message.title}
</Link>
</li>
);
})}
</ul>
<hr />
{/* 指定路由组件展示的位置 */}
<Outlet />
</div>
);
}
Detail.jsx
/* Detail.jsx */
import React from "react";
import {
useParams,
useMatch,
useSearchParams,
useLocation,
} from "react-router-dom";
export default function Detail() {
// 使用params传参
// const params = useParams();
// 使用match传参
// const match = useMatch("/home/message/detail/:id/:title/:content");
// 使用search传参
// const [search, setSearch] = useSearchParams();
// 使用location获取state参数
const location = useLocation();
const stateParams = location.state;
console.log(location);
return (
<div>
{/* <button
onClick={() => {
setSearch("id=haha&title=haha&content=haha");
}}
>
点我更新search参数
</button> */}
<ul>
{/* 使用params传参 */}
{/* <li>id:{params.id}</li>
<li>title::{params.title}</li>
<li>content:{params.content</li> */}
{/* 使用match传参 */}
{/* <li>id:{match.params.id}</li>
<li>title::{match.params.title}</li>
<li>content:{match.params.content</li> */}
{/* 使用search传参 */}
{/* <li>id:{search.get("id")}</li>
<li>title:{search.get("title")}</li>
<li>content:{search.get("content")}</li> */}
{/* 使用state传参 */}
<li>id:{stateParams.id}</li>
<li>title:{stateParams.title}</li>
<li>content::{stateParams.content}</li>
</ul>
</div>
);
}
编程式路由导航
借助useNavigate钩子实现编程式路由跳转
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
// 跳转
function showDetail(message) {
navigate("detail", {
replace: false, // 本身false就是默认值
state: {
id: message.id,
title: message.id,
content: message.content,
}
});
}
前进后退
import React from "react";
import { useNavigate } from "react-router-dom";
export default function Header() {
const navigate = useNavigate();
function back(){
navigate(-1)
}
function forward(){
navigate(1)
}
return (
<div>
<h2>React Router Demo</h2>
<button onClick={back}>后退</button>
<button onClick={forward}>前进</button>
</div>
);
}
useInRouterContext
判断当前路由是否处于路由上下文中,即当前组件是否包含在BrowserRouter或者HashRouter的标签中,一般来讲都是直接在App标签外包一层Router类标签,所以没啥用,除非引入了第三方组件库等情况。
useNavigationType
返回当前的导航类型,即用户是如何来到当前页面的,会返回POP、PUSH、REPLACE中的一个,POP指的是直接在浏览器中直接打开了这个路由组件(或刷新页面)。
useOutlet
用来呈现当前组件中渲染的嵌套路由,如果嵌套路由没有挂载,则result为null,如果嵌套路由已经挂载则展示嵌套的路由对象。
useResolvedPath
给定一个URL值,解析其中的path、search、hash值。
案例
需求
目录
代码
index.js,和5的教程没有太大变化
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./App";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
routes/index.js,涉及了路由表的配置
import Home from "../pages/Home";
import About from "../pages/About";
import Message from "../pages/Message";
import News from "../pages/News";
import Detail from "../pages/Detail";
import { Navigate } from "react-router-dom";
export default [
{
path: "/about",
element: <About />,
},
{
path: "/home",
element: <Home />,
children: [
{
path: "message",
element: <Message />,
children: [
{
// 传递params和match参数时
// path:'detail/:id/:title/:content',
path: "detail",
element: <Detail />,
},
],
},
{ path: "news", element: <News /> },
],
},
{
path: "/",
element: <Navigate to="/about" />,
},
];
App.jsx,涉及路由表的使用
import React from "react";
import { NavLink, useRoutes } from "react-router-dom";
import routesMap from "./routes";
import Header from "./components/Header";
export default function App() {
function computeClass({ isActive }) {
return isActive ? "list-group-item highlight" : "list-group-item";
}
const element = useRoutes(routesMap);
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<Header />
</div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 在React中靠路由链接实现切换组件 */}
<NavLink className={computeClass} to="/about">
About
</NavLink>
{/* 如果希望匹配到子路由时当前链接不高亮,可以加上end属性 */}
<NavLink className={computeClass} end to="/home">
Home
</NavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
{/* <Routes>
<Route path="/about" element={<About />} />
<Route path="/home" element={<Home />} />
<Route path="/" element={<Navigate to="about" />}></Route>
</Routes> */}
{/* 更简单的写法
优化:可以在外部写一个路由表然后再引入 */}
{element}
</div>
</div>
</div>
</div>
</div>
);
}
Header.jsx,涉及前进后退功能
/* Header.jsx */
import React from "react";
import { useNavigate } from "react-router-dom";
export default function Header() {
const navigate = useNavigate();
function back(){
navigate(-1)
}
function forward(){
navigate(1)
}
return (
<div>
<h2>React Router Demo</h2>
<button onClick={back}>后退</button>
<button onClick={forward}>前进</button>
</div>
);
}
About.jsx,用了一下useState钩子
/* About.jsx */
import React from "react";
export default function About() {
const [sum, setSum] = React.useState(1)
return <div>About</div>;
}
Home.jsx,涉及了Outlet的使用
/* Home.jsx */
import React from "react";
import { NavLink, Outlet } from "react-router-dom";
export default function Home() {
return (
<div>
<h1>Home</h1>
<div>
<ul className="nav nav-tabs">
<li>
<NavLink className="list-group-item" to="message">
News
</NavLink>
</li>
<li>
<NavLink className="list-group-item" to="news">
News
</NavLink>
</li>
</ul>
{/* 指定路由组件呈现的位置 */}
<Outlet />
</div>
</div>
);
}
Message.jsx,涉及了React Router 5.x的路由传参
import React, { useState } from "react";
import { Link, Outlet, useNavigate } from "react-router-dom";
export default function Message() {
const navigate = useNavigate();
const [messages] = useState([
{ id: 1, title: "消息1", content: "内容1" },
{ id: 2, title: "消息2", content: "内容2" },
{ id: 3, title: "消息3", content: "内容3" },
]);
// 跳转
function showDetail(message) {
// 只支持replace和state
// 如果希望使用search、match、params需要手动拼在url中
navigate("detail", {
replace: false, // 本身false就是默认值
state: {
id: message.id,
title: message.id,
content: message.content,
},
});
}
return (
<div>
<ul>
{messages.map((message) => {
return (
// 路由链接
<li key={message.id}>
<Link
// 使用params/match传参
// to={`detail/${message.id}/${message.title}/${message.content}`}
// 使用search传参
// to={`detail?id=${message.id}&title=${message.title}&content=${message.content}`}
// 使用state传参
to="detail"
state={{
id: message.id,
title: message.title,
content: message.content,
}}
>
{message.title}
</Link>
<button
onClick={() => {
showDetail(message);
}}
>
查看详情
</button>
</li>
);
})}
</ul>
<hr />
{/* 指定路由组件展示的位置 */}
<Outlet />
</div>
);
}
Detail.jsx 涉及了接收路由参数
import React from "react";
import {
useParams,
useMatch,
useSearchParams,
useLocation,
} from "react-router-dom";
export default function Detail() {
// 使用params传参
// const params = useParams();
// 使用match传参
// const match = useMatch("/home/message/detail/:id/:title/:content");
// 使用search传参
// const [search, setSearch] = useSearchParams();
// 使用location获取state参数
const location = useLocation();
const stateParams = location.state;
console.log(location);
return (
<div>
{/* <button
onClick={() => {
setSearch("id=haha&title=haha&content=haha");
}}
>
点我更新search参数
</button> */}
<ul>
{/* 使用params传参 */}
{/* <li>id:{params.id}</li>
<li>title::{params.title}</li>
<li>content:{params.content</li> */}
{/* 使用match传参 */}
{/* <li>id:{match.params.id}</li>
<li>title::{match.params.title}</li>
<li>content:{match.params.content</li> */}
{/* 使用search传参 */}
{/* <li>id:{search.get("id")}</li>
<li>title:{search.get("title")}</li>
<li>content:{search.get("content")}</li> */}
{/* 使用state传参 */}
<li>id:{stateParams.id}</li>
<li>title:{stateParams.title}</li>
<li>content::{stateParams.content}</li>
</ul>
</div>
);
}
总结
- BrowserRouter、HashRouter和5版本一样
- Routes和Route
- Routes替代了原来的Swtich,并提供了嵌套路由的功能,useRoutes提供了配置路由表的功能,可以与Outlet使用路由表配置嵌套路由。
- Link与NaviLink,Link和5版本一样。NavLink的className提供了回调的写法,可以指定高亮时的样式名,并且可以加一个end路由,使得在子NavLink被选中时只有子组件高亮,而自身不高亮。
- Navigate,只要渲染到页面上,就会修改页面路径,替代了原来的Redirect,并可以指定跳转模式是否为replace。
- Outlet,和Vue的router-view相似
- useRoutes,创建嵌套路由表。
- useNavigate,返回一个函数用来实现编程式导航。
- useParams,返回当前匹配路由的params参数,类似5版本中的match.params
- useSearchParams,接收到的是一个数组,不能直接使用,数组第一个元素要借助其实例的get方法拿到值,第二个元素提供了修改url中search参数的能力。
- useLocation,获取当前location信息,替代了5版本中类式组件的location属性。
- useMatch,返回当前匹配信息,替代5版本中类式组件的match属性。
- useNavigationType、useOutlet、useResolvePath是新的方法。