版本5
安装:
npm
npm install react-router-dom@5 -S
yarn
yarn add react-router-dom@5
import ReactDOM from "react-dom/client";
// react router适用于web和原生项目,我们在web项目中使用,所以需要引入的包是react-router-dom
import {BrowserRouter, Link, Route, Switch} from "react-router-dom";
const Home = () => {
return <div>这是首页</div>;
};
const About = () => {
return <div>关于</div>
};
const App = () => {
return <div>
<ul>
<li>
<Link to="/home">首页</Link>
</li>
<li>
<Link to="/about">关于</Link>
</li>
</ul>
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
</Switch>
</div>;
};
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App/>
</BrowserRouter>
);
Route组件
通过该组件将url地址和React组件进行映射,映射后当url地址变为指定地址时指定的组件就会显示,否则不显示。
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
Route组件可以设置以下几个属性
path
用来设置要映射的路径,可以是一个字符串或字符串数组。字符串用来匹配单个路径,数组可以匹配多个路径。看一个数组的例子:
<Route path={["/about", "/hello"]}>
<About/>
</Route>
使用数组映射后,当我们访问数组中的路径时都会使组件挂载。设置路径时也可以在路径中设置参数,比如:/student/:id其中id就是一个参数,可以动态的传递:id的值,换句话说/student/1或/student/2,都会触发组件挂载。
设置动态参数后,在组件的内部可以使用useParams()钩子来读取参数:
const Student = () => {
const {id} = useParams();
return <div>学生id:{id}</div>
};
<Route path="/student/:id">
<Student/>
</Route>
exact
路由的匹配默认并不是完整匹配,这意味着路由映射的地址是/home时,只要我们访问的路径是以/home开头就会触发组件的挂载,默认情况下是不会检查路由的子路径的。比如:/home/hello、/home/abc等都会导致home组件挂载。
exact属性用来设置路由地址是否完整匹配,它需要一个布尔值,默认为false,就像上边的情况。如果设置为true,那么只有地址和path完全一致时,组件才会挂载。
<Route path="/home" exact>
<Home/>
</Route>
这样一来只有访问/home时,home组件才会挂载,差一个字母都不行哦!
strict
布尔值,默认值为false。false时,会匹配到以/结尾的路径。比如:path设置为/home默认情况下/home/也会导致组件挂载。设置为true时,以/结尾的路径不会被匹配。
component
设置路径匹配后需要挂载的组件。作用和Route的标签体类似。
<Route path="/home" component={Home}/>
和标签体指定组件不同,如果通过component属性指定组件,React Router会自动向组件中传递三个参数match、location和history。
match
对象,表示请求匹配的路径信息,其中包含四个属性:
param —— 请求参数
isExact —— 布尔值,请求路径是否完整匹配
path —— 请求路径的规则
url —— 匹配到的url地址
location
对象,表示浏览器地址栏的信息,请求完整路径、查询字符串等,可能具有的属性:
pathname —— 请求的路径
search —— 查询字符串
hash —— hash字符串
state —— 历史记录中的状态对象,可以用来在跳转时传递数据
history
对象,用来读取和操作浏览器的历史记录(页面跳转)等功能,属性:
length —— 历史记录的数量
action —— 当前历史记录的状态,pop(前进、后退、新记录创建、索引发生变化);push(新记录添加);replace(历史记录被替换)
location —— location对象
push() —— 添加新的历史记录
replace() —— 替换历史记录
go() —— 跳转到指定记录
goBack() —— 回退
goForward() —— 前进
block() —— 用来阻止用户跳转行为,可以用Prompt组件代替
About.js
import React from "react";
const About = (props) => {
const clickHandler = () => {
props.history.replace({
pathname: "/student/2",
state: { name: "哈哈" },
});
};
return (
<div>
<button onClick={clickHandler}>点击</button>
</div>
);
};
export default About;
Student.js
import React from 'react';
const STU_DATA = [
{
id:1,
name:'孙悟空'
},
{
id:2,
name:'猪八戒'
},
{
id:3,
name:'沙和尚'
},
{
id:4,
name:'唐僧'
},
];
const Student = (props) => {
const stu = STU_DATA.find(item => item.id === +props.match.params.id);
return (
<div>
{stu.id} --- {stu.name}
</div>
);
};
export default Student;
App.js
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/>
{/*
component用来指定路由匹配后被挂载的组件
component需要直接传递组件的类
通过component构建的组件,它会自动创建组件并且会自动传递参数
match -- 匹配的信息
isExact 检查路径是否完全匹配
params 请求的参数
location -- 地址信息
history -- 控制页面的跳转
push() 跳转页面
replace() 替换页面
*/}
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
{/*
/student/:id 会匹配到 /student/xxx
*/}
<Route path="/student/:id" component={Student}/>
</div>
);
}
export default App;
5.render
render也是Route组件中的属性,和component类似,也用来指定路径匹配后需要挂载的组件。只是render需要的是一个回调函数作为参数,组件挂载时,render对应的回调函数会被调用,且函数的返回值会成为被挂载的组件。render的回调函数中会接收到一个对象作为参数,对象中包含三个属性,即match、location和history,我们可以根据需要选择是否将其传递给组件。
<Route path="/student/:id" render={routeProps => <Student {...routeProps}/>} />
routeProps 就是 historys location match
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>
);
};
6.children
children实际上就是组件的组件体,设置方式有两种一个是通过组件体设置,一个是通过children属性设置。它的值也有两种方式,一种直接传递组件,这样当路径匹配时组件会自动挂载。一种是传递一个回调函数,这样它和render的特点是一样的。
直接设置组件:
<Route path="/student/:id" children={<Student/>} />
<Route path="/student/:id">
<Student/>
</Route>
传递回调函数:
<Route path="/student/:id" children={routeProps => <Student {...routeProps}/>} />
<Route path="/student/:id">
{routeProps => <Student {...routeProps}/>}
</Route>
需要注意的时,当children接收到的是一个回调函数时,即使路径没有匹配组件也会被挂载到页面中(没有使用Switch标签的情况下),这一特性可以在一些特殊应用场景下发挥作用。如果不希望出现路径不匹配时组件被挂载的情况,最好选择使用render来代替。
Switch组件
Switch组件是Route组件的外部容器,可以将Route组件放入到Switch组件中。放入Switch组件中后,匹配路径时会自动自上向下对Route进行匹配,如果匹配到则挂载组件,并且一个Switch中只会有一个Route被挂载。如果将Route组件单独使用,那么所有的路径匹配的Route中的组件都会被挂载。
Link组件
Link组件作用类似于a标签(超链接),并且Link组件在浏览器中也会被渲染为超链接。但是Link组件生成的链接点击后只会修改浏览器地址栏的url,并不会真的向服务器发送请求。这种方式有利于组件的渲染,所以在开发中应该使用Link组件而不是超链接。
HashRouter组件
除了BrowserRouter以外,react router中还为我们提供了HashRouter,它是干什么用的呢?其实很简单,当我们使用BrowserRouter时,路径会直接根据url地址进行跳转,也就是我们在使用应用时在浏览器的地址栏看到的地址就和我们正常去访问网页一样。
但是,HashRouter不是这样,使用HashRouter时,组件的跳转不再是以完整的url形式,而是通过url地址中的hash值进行跳转(url地址中#后的内容为hash值)。
BrowserRouter的地址栏
HashRouter的地址栏
为什么会有这两种Router呢?首先,你要明确我们的项目在开发完成后需要进行构建,构建后的代码需要放到服务器中,以供用户访问。服务器无非就是Nginx或Apache这些东西,服务器的主要功能是将url地址和网页进行映射。传统web项目中,每一个页面都对应一个文件,当用户访问/index.html时,服务器会自动返回根目录下的index.html。当用户访问/about.html时,服务器会返回根目录下about.html。换句话说url和文件的映射都是由服务器来完成的。
但是React项目不同,React项目所有的页面都是通过React进行渲染构建的。项目中只存在一个index.html没有那么多的页面(所以才叫单页应用)。当浏览器地址发生变化时,比如用户访问/about时,此时是不需要服务器介入的,react router会自动挂载对应的组件。
当我们将React项目部署到服务器时,如果直接访问根目录,请求会直接发送给index.html。这个页面我们是有的,所以此时不会有任何问题。用户访问页面后,点击页面后的连接切换到不同的组件也没有问题,因为页面并没有真的发生跳转,而是通过react router在内存中完成了模拟跳转。但是,当我们刷新某个路由或直接通过浏览器地址栏访问某个路由时,比如:http://localhost:3000/about,此时请求会发送给服务器,服务器会寻找名为about的资源(此时并没有经过React)。显然找不到这个资源,于是返回404。
这样一来,我们的项目只能够通过首页访问,然后点击链接跳转,刷新和直接通过路由访问都是不行的,一旦进行这些操作就会出现404。
怎么办呢?两种解决方式:
使用HashRouter,HashRouter通过hash地址跳转,而服务器不会处理hash地址,这样地址就会交由React处理,路由便可正常跳转。缺点是url地址上总会多出一个#,但不妨碍使用。
修改服务器映射规则,将所有的请求交给React处理,禁止服务器自动匹配页面。以nginx为例,可以将nginx.conf中的配置信息修改如下:
location / {
root html;
try_files $uri /index.html;
}
两种方式都可以解决404的问题,具体采用那种方案,需要根据你自己项目的实际情况选择。
NavLink组件
特殊版本的Link,可以根据不同的情况设置不同的样式。
属性:
activeClassName —— 字符串 链接激活时的class
activeStyle —— 对象 链接激活时的样式
isActive —— 函数,可动态判断链接是否激活
style —— 函数,动态设置样式
className —— 函数,动态设置class值
Redirect组件
将请求重定向到一个新的位置,经常用来进行权限的处理。例如:当用户已经登录时则正常显示组件,用户没有登录时则跳转到登录页面。
{isLogin && <SomeAuthComponent/>}
{!isLogin && <Redirect to={"/login"}></Redirect>}
上例中,如果isLogin的值为true,表示用户已经登录,若用户登录,则挂载对应组件。若isLogin值为false,则挂载Redirect组件触发重定向,重定向会使得路径跳转到登录页面。
属性:
to —— 重定向的目标地址,可以是一个字符串也可以是一个对象
from —— 需要重定向的地址
push —— 布尔值,是否使用push方式对请求进行重定向
版本6
安装:
npm
npm install react-router-dom@6 -S
yarn
yarn add react-router-dom@6
import React from ‘react’;
import ReactDOM from ‘react-dom/client’;
import { BrowserRouter as Router, Link, Route, Routes } from ‘react-router-dom’;
const Home = ()=>{
return <div>首页</div>
};
const About = () => {
return <div>关于</div>
};
const App = () => {
return <div>App
<ul>
<li>
<Link to=”/”>home</Link>
</li>
<li>
<Link to=”/about”>about</Link>
</li>
</ul>
<Routes>
<Route path=”/” element={<Home/>}/>
<Route path=”/about” element={<About/>}/>
</Routes>
</div>;
};
const root = ReactDOM.createRoot(document.getElementById(‘root’));
root.render(
<Router>
<App />
</Router>
);
Route组件
Route作用和版本5的一样,只是变得更简单了,没有了那么多复杂的属性,并且Route组件必须放到Routes中,当浏览器的地址发生变化时,会自动对Routes中的所有Route进行匹配,匹配到的则显示,其余Route则不再继续匹配。可以将Route当成是一个类似于if语句的东西,路径(path)匹配则其中的组件便会被渲染。
path —— 要匹配的路径
element —— 路径匹配后挂载的组件,直接传JSX
index —— 布尔值,路由是否作为默认组件显示
Outlet组件
Outlet组件用来在父级路由中挂载子路由。
在版本6中Route组件是可以嵌套的,可以通过嵌套Route来构建出嵌套路由,像这样:
<Route path='/students' element={<StudentList/>}>
<Route path=':id' element={<Student/>}/>
</Route>
上例中,Route嵌套后,如果访问/students则会挂载StudentList组件,如果访问/students/:id则会自动在StudentList组件中对Student组件进行挂载。在StudentList组件中就可以使用Outlet来引用这些被挂载的组件。
const StudentList = () => {
return <div>
学生列表
<Outlet/>
</div>
};