这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天
路由是前端框架必备的知识点,了解路由能帮我们更加轻松地管理代码,提高项目的可维护性。如今 ReactRouter 版本默认为6,这个笔记也讲解了5的使用,可以帮我们管理一些老项目。
React Router
1. V5
react router 可以将 url 地址和组件进行映射。当用户访问某个地址时,与其对应的组件会自动的挂载
安装: npm i react-router-dom@5 -S
1.1 HashRouter 和 BrowserRouter
HashRouter 会通过url地址中的hash值来对地址进行匹配
HashRouter 对于 seo 不是很友好
BrowserRouter 直接通过url地址进行组件的跳转
使用过程中和普通的url地址没有区别
BrowserRouter 问题在于当我们通过 标签跳转时,刷新页面会重新向服务器请求数据,这时候页面会返回404,因为这次请求没有经过 react router。
解决方法:
- 使用 HashRouter,服务器不会判断 hash 值
- 修改服务器的配置(nginx),将所有请求都转发到 index.html
1.2 具体使用
在 main.jsx 中使用
-
引入 react-router-dom 包
// 这里重命名是为了以后更换路由模式更加方便 import { BrowerRouter as Router } from 'react-router-dom' -
将 组件包裹
<Router> <App /> </Router> -
最终代码
// main.jsx import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import { BrowserRouter as Router } from 'react-router-dom' ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <Router> <App /> </Router> </React.StrictMode>, )
在 App.jsx 和 其他组件 中使用
-
引入 Route 标签和组件
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' -
配置路由
配置路由有很多方式,依次介绍
2.1 component 方式:
<Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/student/:id" component={Student} />属性 作用 path 路由映射的 url 地址,其中上述例子中的 :id为 uri 参数component 路由要挂载的组件 exact 路径是否完全匹配,React Router5 默认不是完全匹配,例如指定 path=“/about”,访问.../about/hello也可以看到被挂载的组件这时在
Student组件中就可以访问到路由参数了import React from 'react' const STU_DATA = [ { id: 1, name: 'xj' }, // ... ] 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> ) } export default Student2.2 render 方式
render 也可以用来指定要挂载的组件,不过需要一个回调函数作为参数,回调函数的返回值最终会被挂载
注意: 如果希望被挂载的组件访问到路由参数,一定要指定组件的路由属性
<Route path="/student/:id" render={(routeProps) => { console.log(routeProps) return <Student {...routeProps} /> {/* 一定要这样传递参数,不然 Student 组件无法访问路由参数 */} }} />2.3 children 方式
children 也可以用来指定被挂载的组件,用法有两种:
- 和 render 类似,传递回调函数,注意:当children设置一个回调函数时组件无论路径是否匹配都会挂载,
所以不建议 - 可以传递组件
<Route path="/student/:id" children={(routeProps) => <Student {...routeProps} />} /> <Route path="/student/:id" children={<Student />} />2.4 直接写组件
<Route path="student/:id"> <Student /> </Route>这种方式更加清晰,但需要注意的是,
Student组件是无法直接打印 props 来获取路由参数的,但是我们可以通过钩子函数,这也是更加常用的情况import React from 'react' import { useHistory, useLocation, useParams, useRouteMatch } from 'react-router-dom' const STU_DATA = [ { id: 1, name: 'xj' }, // ... ] const Student = (props) => { // 通过钩子函数获取路由参数 const match = useRouteMatch() const location = useLocation() const history = useHistory() const params = useParams() const stu = STU_DATA.find(item => item.id === +params.id) return ( <div> {stu.id} --- {stu.name} </div> ) } export default Student - 和 render 类似,传递回调函数,注意:当children设置一个回调函数时组件无论路径是否匹配都会挂载,
-
路由参数
参数名 作用 match 匹配的信息 location 路由地址信息 history 控制页面的跳转 3.1 match 参数
参数名 类型 含义 isExact Boolean 是否与指定路径完全匹配 params 对象 路由传递的参数,例如 student/:id中的id便是params的一个属性path String 指定路由的地址,例如 <Route path=“/student/:id” />,那么 path 就是“/student/:id”url String 浏览器实际访问的路由地址,例如访问 http://127.0.0.1/student/3,那么 url 就是“/student/3”3.2 location 参数
参数名 类型 含义 hash 字符串 hash地址 pathname 字符串 与 match 的 path 参数一致 search 字符串 查询字符串,即?之后的参数 state 用户指定类型 使用 history.push({path: ‘/student/2’, state: {msg: ‘沈昕姐姐,嘿嘿’}}),手动跳转地址时,state会显示3.3 history 参数
参数名 类型 作用 push Function 跳转页面,带后退前进功能 replace Function 也是跳转页面,不过没有后退前进功能
# Switch 标签
可以将 Route 统一放到 Switch 中,一个 Switch 中只会有一个路由显示
注意:根标签要加上 exact,不然只显示 Home 组件了
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about">
<About />
</Route>
<Route path="/login">
<Login />
</Route>
<Route path="/student/:id">
<Student />
</Route>
</Switch>
# Redirect
Redirect 用于重定向,例如未成功登录强制跳转到登录页面,登陆成功跳转到内容页面
<Route path="/form">
{ isLogin ? <MyForm /> : <Redirect to={"/login"} /> }
</Route>
当然也可以指定 哪个路由跳转到哪个路由,例如下面指的是当访问路由地址为 /abc 时跳转到 /login,访问其他路由地址就不会跳转
<Redirect from={"/abc"} to={"/login"} />
# 路由嵌套
在 App.jsx 中使用
// App.jsx
import React from 'react'
import { Route } from 'react-router-dom'
import About from './components/About'
import Hello from './components/Hello'
const App = () => {
return (
<div>
<Route path="/about">
<About />
<Route path="/about/hello">
<Hello />
</Route>
</Route>
</div>
)
}
export default App
当然也可以直接在 About 组件中嵌套
import React from 'react'
import { Redirect, Route, useRouteMatch } from 'react-router-dom'
import Hello from './Hello'
const About = (props) => {
// 获取父路径
const { path } = useRouteMatch()
return (
<div>
<h2>关于我们</h2>
<ul>
<li>xj</li> <li>sx</li> <li>yq</li> <li>xm</li>
</ul>
{/* 子路由嵌套 */}
<Route path={`${path}/hello`}>
<Hello />
</Route>
</div>
)
}
export default About
# 路径不匹配
如果用户随便输入了路径地址,此时要做一定的处理
<Route path="*">
路径匹配错误
</Route>
4.1.3 总结
一般使用组件嵌套 + 钩子函数组合使用
// main.jsx
import React from 'react'
import { Route } from 'react-router-dom'
import About from './components/About'
// ...
const App = () => {
return (
<div className="APP">
<Menu />
<Route path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/student/:id">
<Student />
</Route>
</div>
)
}
export default App
// student.jsx
import React from 'react'
import { useHistory, useLocation, useParams, useRouteMatch } from 'react-router-dom'
const STU_DATA = [
{ id: 1, name: 'xj' },
// ...
]
const Student = () => {
const match = useRouteMatch()
const location = useLocation()
const history = useHistory()
const params = useParams()
const stu = STU_DATA.find(item => item.id === +params.id)
return (
<div>
{stu.id} --- {stu.name}
</div>
)
}
export default Student
2. V6
2.1 具体使用
React Router 6 更改的特性有很多,例如:
<Route />组件必须由<Routes />组件包裹- 指定被挂载的组件需要使用 JSX 形式,如
element={<Xxx />} path指定路径可以不用加 /- 子路由也不用加上父路由根路径
// main.jsx
<Routes>
<Route path="student/:id" element={<Student />}></Route>
<Route path="about" element={<About />}>
{/* Hello组件路由地址为: /about/hello */}
<Route path="hello" element={<Hello />}></Route>
{/* Abc组件路由地址为: /about/abc */}
<Route path="abc" element={<Abc />}></Route>
</Route>
</Routes>
组件中的使用,以 About 组件为例,只需要加入 <Outlet /> 组件,即可在此区域显示子路由内容
Outlet 用来标识路由中的组件:
- 当嵌套路由中的路径匹配成功了,Outlet则表示嵌套路由中的组件
- 当嵌套路由中的路径没有匹配成功,Outlet就什么都不会显示
import React from 'react'
import { Routes, Route, Outlet } from 'react-router-dom'
const About = () => {
return (
<div>
<h2>这是About组件</h2>
<Outlet />
</div>
)
}
export default About
当然也可以使用另一种方法
// main.jsx
<Routes>
<Route path="about" element={<About />}></Route>
</Routes>
About 组件:
// component/About.jsx
import React from 'react'
import Hello from './Hello'
import { Routes, Route } from 'react-router-dom'
const About = () => {
return (
<div>
{/* 通过子路由来对 Hello 进行映射, /about/hello */}
<Routes>
<Route path="hello" element={<Hello />} />
</Routes>
</div>
)
}
export default About
# Navigate 组件
Navigate 组件与 V5 版本的 Redirect 组件类似,用来指定重定向
Navigate 默认使用 push 跳转,但是可指定 replace 属性更换为 replace 跳转
<Navigate to="student/1" replace />
# Link 与 NavLink 组件
Link 组件与 V5 版本一致,NavLink 在指定链接样式的时候,需要一个回调函数
回调函数中有一个 isActive 布尔值形参,代表链接是否激活
<NavLink
style={({ isActive }) => {
return isActive ? { backgroundColor: 'red' } : null
}}
to="/home"
>主页</NavLink>