千锋前端-React全家桶_React项目实战全球新闻发布管理系统_哔哩哔哩_bilibili
该项目的路由部分是用react-router V5写的,但是react的路由目前已经更新到V6版本,因此将项目中的路由部分更新了一下。主要包括如下几个方面:
一. <Routes/> 与 <Route/>
1. v6
版本中移出了先前的
<Switch>
,引入了新的替代者:
<Routes>
。
2.
<Routes>
和
<Route>
要配合使用,且必须要用
<Routes>
包裹
<Route>
。
3.
<Route>
相当于一个
if
语句,如果其路径与当前
URL
匹配,则呈现其对应的组件。
4.
<Route caseSensitive>
属性用于指定:匹配时是否区分大小写(默认为
false
)。
5.
当
URL
发生变化时,
<Routes>
都会查看其所有子
<Route>
元素以找到最佳匹配并呈现组件 。
6.
<Route>
也可以嵌套使用,且可配合
useRoutes()
配置
“
路由表
”
,但需要通过
<Outlet>
组件
来渲染其子路由。
结合项目:
V5版本:
export default function IndexRouter() {
return (
<HashRouter>
<Switch>
{/* login,news,detail,/这四个可以拿到路由组件提供的props,但是NewsSandBox拿不到 */}
<Route path="/login" component={Login} />
{/* 下面这两个是作为游客身份登陆时可以访问到的组件 */}
<Route path="/news" component={News} />
<Route path="/detail/:id" component={Detail} />
{/* <Route path="/" component={NewsSandBox}/> */}
</Switch>
</HashRouter>
)
}
V6版本:
export default function IndexRouter() {
return (
<HashRouter>
{/* react-route V6版本中HashRouter 里面包 Switch 会出错,改为包 Routes (Routes 和 Switch 的功用是一样的,都能做到精准匹配) */}
<Routes>
{/* Route 的 component 属性改为 element,并且 element 中使用 <> 包裹组件名称 */}
<Route path="/login" element={<Login />} />
{/* 下面这两个是作为游客身份登陆时可以访问到的组件 */}
<Route path="/news" element={<News />} />
<Route path="/detail/:id" element={<Detail />} />
{/* <Route path="/" component={NewsSandBox}/> */}
</Routes>
</HashRouter>
)
}
二. 重定向Navigate
1.
作用:只要
<Navigate>
组件被渲染,就会修改路径,切换视图。
2.
replace
属性用于控制跳转模式(
push
或
replace
,默认是
push
)。
结合项目:
V5版本:
<Route path="/" render={() =>
localStorage.getItem("token") ?
<NewsSandBox ></NewsSandBox> :
<Redirect to="/login" />
} />
V6版本:
import { Navigate } from 'react-router-dom'
// ...
<Route path="/" element={
localStorage.getItem("token") ?
<NewsSandBox ></NewsSandBox> :
// 重定向时需要将 Redirect 改为 Navigate,并且一样使用的是 element 而不是 render
<Navigate to="/login" />
} />
三. 废除withrouter,使用Hooks
1. useNavigate()
作用:返回一个函数用来实现编程式导航。结合项目:
V5版本:
function SideMenu(props) {
// 使用withroute高阶组件,使该组件可以接受到路由组件传来的props属性
// 获取当前url中的key值,传入Menu中,作为选中/展开条件
....
....
const renderMenu = (menuList) => {
....
return checkPagePermission(item) && <Menu.Item key={item.key} icon={iconList[item.key]} onClick={() => {
props.history.push(item.key)
}}>{item.title}</Menu.Item>
})
}
const selectKeys = [props.location.pathname]
const openKeys = ["/" + props.location.pathname.split("/")[1]]
return (
....
}
export default connect(mapStateToProps)(withRouter(SideMenu))
V6版本:
import { useNavigate, useLocation } from "react-router";
function SideMenu(props) {
let navigate = useNavigate();
......
......
const renderMenu = (menuList) => {
......
return checkPagePermission(item) && <Menu.Item key={item.key} icon={iconList[item.key]} onClick={() => {
navigate(item.key)
}}>{item.title}</Menu.Item>
})
}
// V6写法
let location = useLocation();
const selectKeys = [location.pathname];
const openKeys = ["/" + location.pathname.split("/")[1]];
return (
.......
)
}
2. useParams
1.
作用:回当前匹配路由的
params
参数,类似于
5.x
中的
match.params
。项目中对比:
V5版本:
const [newsInfo, setnewsInfo] = useState(null)
useEffect(() => {
axios.get(`/news/${props.match.params.id}?_expand=category&_expand=role`).then(res => {
setnewsInfo({
...res.data,
// 每刷新一次就把view的值加一
view: res.data.view + 1
})
//同步后端
return res.data
}).then(res => {
axios.patch(`/news/${props.match.params.id}`, {
view: res.view + 1
})
})
}, [props.match.params.id])
const handleStar = () => {
// 本地和数据库都要更新数据
setnewsInfo({
...newsInfo,
star: newsInfo.star + 1
})
axios.patch(`/news/${props.match.params.id}`, {
star: newsInfo.star + 1
})
}
V6版本:
import { useParams } from "react-router";
const params = useParams();
const [refresh, setRefresh] = useState(false);
export default function Detail(props) {
const [newsInfo, setnewsInfo] = useState(null)
let star = localStorage.getItem("star") || [];
useEffect(() => {
axios.get(`/news/${params.id}?_expand=category&_expand=role`).then(res => {
setnewsInfo({
...res.data,
// 每刷新一次就把view的值加一
view: res.data.view + 1
})
//同步后端
return res.data
}).then(res => {
axios.patch(`/news/${params.id}`, {
view: res.view + 1
})
})
}, [refresh])
const handleStar = () => {
if (!star.includes(params.id.toString())) {
updateNews(params.id, {
star: newsInfo.star + 1,
})
.then(() => {
setRefresh();
const arr = [...star];
localStorage.setItem("star", arr.concat(params.id));
})
.catch((e) => console.log(e));
} else {
notification.info({
message: `error`,
description: `starError`,
placement: "bottomRight",
});
}
};
......
}
四. 其它
1. useRoutes()
1.
作用:根据路由表,动态创建
<Routes>
和
<Route>
。
2.
示例代码:
//路由表配置:src/routes/index.js
import About from '../pages/About'
import Home from '../pages/Home'
import {Navigate} from 'react-router-dom'
export default [
{ path:'/about', element:<About/> },
{ path:'/home', element:<Home/> },
{ path:'/', element:<Navigate to="/about"/> }
]
//App.jsx
import React from 'react'
import {NavLink,useRoutes} from 'react-router-dom'
import routes from './routes'
export default function App() {
//根据路由表生成对应的路由规则
const element = useRoutes(routes)
return (
<div>......
{/* 注册路由 */}
{element} ......
</div> )
}
2. useLocation()
1.
作用:获取当前
location
信息,对标
5.x
中的路由组件的
location
属性。
2. 示例代码:
import React from 'react'
import {useLocation} from 'react-router-dom'
export default function Detail() {
const x = useLocation()
console.log('@',x) // x就是location对象:
/* {
hash: "",
key: "ah9nv6sz",
pathname: "/login",
search: "?name=zs&age=18",
state: {a: 1, b: 2}
}*/
return (
<ul>
<li>消息编号:{id}</li>
<li>消息标题:{title}</li>
<li>消息内容:{content}</li>
</ul> )
}
3. useMatch()
1.
作用:返回当前匹配信息,对标
5.x
中的路由组件的
match
属性。
2. 示例代码:
<Route path="/login/:page/:pageSize" element={<Login />}/>
<NavLink to="/login/1/10">登录</NavLink>
export default function Login() {
const match = useMatch('/login/:x/:y')
console.log(match) //输出match对象
//match对象内容如下: /*
{
params: {x: '1', y: '10'}
pathname: "/LoGin/1/10" pathnameBase: "/LoGin/1/10"
pattern: { path: '/login/:x/:y',
caseSensitive: false,
end: false }
} */
return (
<div>
<h1>Login</h1>
</div>
)
}
4. useInRouterContext()
作用:如果组件在
<Router>
的上下文中呈现,则
useInRouterContext
钩子返回
true
,否则
返回
false
。
5. useNavigationType()
1.
作用:返回当前的导航类型(用户是如何来到当前页面的)。
2.
返回值:
POP
、
PUSH
、
REPLACE
。
3.
备注:
POP
是指在浏览器中直接打开了这个路由组件(刷新页面)。
6. useOutlet()
1.
作用:用来呈现当前组件中渲染的嵌套路由。
2.
示例代码:
const result = useOutlet()
console.log(result)
// 如果嵌套路由没有挂载,则result为null
// 如果嵌套路由已经挂载,则展示嵌套的路由对象
7. useResolvedPath
1.
作用:给定一个
URL
值,解析其中的:
path
、
search
、
hash
值。