携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
react-router@6 打通学习
1. HashRouter和BrowserRouter的区别
概述
HashRouter
在路径中包含了#,相当于HTML的锚点定位。(# 符号的英文叫hash,所以叫HashRouter)
BrowserRouter
使用的是HTML5的新特性History,没有HashRouter(锚点定位)那样通用,低版本浏览器可能不支持。
用法的却别
BrowserRouter进行组件跳转时可以传递任意参数实现组件间的通信
HashRouter不能(除非手动拼接URL字符串),因此一般配合Redux使用,实现组件间的数据通信。
HashRouter
1. HashRouter相当于锚点定位,因此不论#后面的路径怎么变化,请求的都相当于是#之前的那个页面。
可以很容易的进行前后端不分离的部署(也就是把前端打包后的文件放到服务器端的public或static里),
因为请求的链接都是ip地址:端口/#/xxxx,因此请求的资源路径永远为/,相当于index.html,
而其他的后端API接口都可以正常请求,不会和/冲突,由于前后端不分离也不会产生跨域问题。
2. 缺点就是丑,路径里总有个#
BrowserRouter
1. 因为BrowserRouter模式下请求的链接都是ip地址:端口/xxxx/xxxx,
因此相当于每个URL都会访问一个不同的后端地址,如果后端没有覆盖到路由就会产生404错误。
2. 可以通过加入中间件解决,放在服务器端路由匹配的最后,如果前面的API接口都不匹配,
则返回index.html页面。但这样也有一些小问题,因为要求前端路由和后端路由的URL不能重复。
3. 比如商品列表组件叫/product/list,而请求商品列表的API也是/product/list,那么就会访问不到页面,
而是被API接口匹配到。
解决方法:
进行前后端分离的部署,比如前端地址ip1:端口1,后端接口地址ip2:端口2,
使用Nginx反向代理服务器进行请求分发。
前端向后端发起请求的URL为nginx所在的服务器+/api/xxx,通过NGINX的配置文件判断,
如果URL以api开头则转发至后端接口,否则转发至前端的地址,访问项目只需访问Nginx服务器即可。
2. Routes and Route
1. <Routes> and <Route> are the primary ways to render something in React
2. Router based on the current location,Whenever the location changes,
<Routes> looks through all its children <Route> elements to find the best match and
renders that branch of the UI.
// App.tsx
import { Routes, Route } from 'react-router-dom'
import React, { Component } from 'react'
const AboutPage = () => {
return (
<div
style={{
width: '200px',
height: '200px;',
textAlign: 'center',
padding: '10px'
}}
>
<div
style={{
background: '#ccc',
marginBottom: '20px'
}}
>
react-router@6 落地
</div>
<Outlet />
</div>
)
}
const DetailPage = () => {
return <div>详情</div>
}
const DataList = () => {
return <div>列表</div>
}
const App = () => {
return (
<HashRouter>
<Routes>
<Route path="/" element={<AboutPage />}>
<Route path="detail" element={<DetailPage />} />
<Route path="list" element={<DataList />} />
</Route>
</Routes>
</HashRouter>
)
}
如你所见,简而言之:Route根据当前路由的变化渲染不同的组件,必须要被Routes包裹
如果需要配置动态路由
<Routes>
<Route path="/" element={<AboutPage />}>
<Route path="detail">
<Route path=":id" element={<DetailPage />} />
</Route>
<Route path="list" element={<DataList />} />
</Route>
</Routes>
默认路由
<Routes>
<Route path="/" element={<AboutPage />}>
<Route index element={<DataIndex />} />
<Route path="detail">
<Route path=":id" element={<DetailPage />} />
</Route>
<Route path="list" element={<DataList />} />
</Route>
</Routes>
在嵌套路由中,如果 URL 仅匹配了父级 URL,则Outlet中会显示带有index属性的子路由。
3. Outlet
An <Outlet> should be used in parent route elements to render their child route elements.
如上面的实例 可以看出:
Outlet组件,用于父组件中可以为子路由的元素占位,并最终渲染子路由的元素。
4. Navigate
A <Navigate> element changes the current location when it is rendered.
页面重定向等价于以前版本中的Redirect组件
const DetailPage = () => {
return <div>详情</div>
}
const DataList = () => {
return <div>列表</div>
}
const DataIndex = () => {
return <Navigate to="list" replace={true} />
}
const App = () => {
return (
<HashRouter>
<Routes>
<Route path="/" element={<AboutPage />}>
<Route index element={<DataIndex />} />
<Route path="detail">
<Route path=":id" element={<DetailPage />} />
</Route>
<Route path="list" element={<DataList />} />
</Route>
</Routes>
</HashRouter>
)
}
5. Link
A <Link> is an element that lets the user navigate to another page by clicking or tapping on it.
const DetailPage = () => {
return <div>详情</div>
}
const DataList = () => {
return <div>列表</div>
}
const DataIndex = () => {
return (
<div>
<div>
<Link to="list">去列表</Link>
</div>
<div>
<Link to="detail/1">详情</Link>
</div>
</div>
)
}
const App = () => {
return (
<HashRouter>
<Routes>
<Route path="/" element={<AboutPage />}>
<Route index element={<DataIndex />} />
<Route path="detail">
<Route path=":id" element={<DetailPage />} />
</Route>
<Route path="list" element={<DataList />} />
</Route>
</Routes>
</HashRouter>
)
}
6. NavLink
A <NavLink> is a special kind of <Link> that knows whether or not it is "active".
This is useful when building a navigation menu such as a breadcrumb or a set of tabs
where you'd like to show which of them is currently selected.
const AboutPage = () => {
return (
<div
style={{
width: '200px',
height: '200px',
textAlign: 'center',
padding: '10px' }}
>
<div style={{
background: '#ccc',
marginBottom: '20px'
}}
>
react-router@6 落地
</div>
<NavLink
to="list"
style={({ isActive }) => ({
color: isActive ? 'red' : 'blue'
})}
>
list
</NavLink>
<span style={{ marginLeft: '40px' }}>
<NavLink
to="detail/1"
style={({ isActive }) => ({
color: isActive ? 'red' : 'blue' })}
>
detail
</NavLink>
</span>
<Outlet />
</div>
)
}
const DetailPage = () => {
return <div>详情</div>
}
const DataList = () => {
return <div>列表</div>
}
const DataIndex = () => {
return (
<div>
<div>
<Link to="list">去列表</Link>
</div>
<div>
<Link to="detail/1">详情</Link>
</div>
</div>
)
}
const App = () => {
return (
<HashRouter>
<Routes>
<Route path="/" element={<AboutPage />}>
<Route index element={<DataIndex />} />
<Route path="detail">
<Route path=":id" element={<DetailPage />} />
</Route>
<Route path="list" element={<DataList />} />
</Route>
</Routes>
</HashRouter>
)
}
7. 路由js 跳转
根据ajax请求返回值跳转不同的页面,这时我们就得使用js的方式时行跳转了,虽然新版的react-router已经移除掉history对象,但给我们提供了 useNavigate() hook实现路由跳转,
import { useNavigate } from "react-router-dom";
let navigate = useNavigate();
navigate(`/home`);
// 跳转且不保留浏览记录
navigate(`/home`,{replace:true});
// 返回上一页
navigate(-1)
// 对象方式跳转
navigate({
pathname:'/home'
})
需要注意一点就是,在v6版本的react-router中,如果跳转的路径如果不是以/开头,则为相对路径,相对于其父级路由路径,这样的设置能让我们更好的控制跳转
路由传参
在进行路由跳转时,可以通过附带一些参数一起传递到目标页面来实现某些特殊功能,但新版的react-router已经从props中移除了history、location、match,也移除掉了withRouter高阶组件,所以无法使用老版本的方式传参与接收,新版用法如下:
search传参
import { useNavigate } from "react-router-dom";
let navigate = useNavigate();
navigate(`/home?pageIndex=1&pageSize=10`);
navigate({
pathname:'/home',
search:'pageIndex=1&pageSize=10'
});
接受参数
在对应组件接收参数,使用useSearchParams hook进行接收,得到URLSearchParams对象以及设置search参数函数组成的数据
function Home(){
const [searchParams,setSearchParams] = useSearchParams()
searchParams.get('pageIndex'); // 1
searchParams.get('pageSize'); // 10
return (
<div>首页</div>
)
}