一、简介
react router v6发布了三个不同的包:
-
react-router:路由核心库,提供许多组件、钩子;
-
react-router-dom: 包括了 react-router 所有内容,同时添加了用于 DOM 的组件;
-
react-router-native: 包括了 react-router 所有内容,同时添加了用于 ReactNative 的 API
与react-router 之前版本区别:
-
内置组件的变化:移除
<Switch/>
,新增<Routes/>等内置组件
-
语法变化:
component={}
变成element={}
-
新增 hook:useRoutes、
useParams
、useNavigate
、useMatch等等
-
官方明确表示推荐使用函数式组件
二、创建路由表
// src/router/index.tsx
import { Suspense, lazy } from 'react'
import Loading from '@/components/Loading'
import type { RouteObject } from 'react-router-dom'
// 路由懒加载
const lazyLoad = (path: string): React.ReactNode => {
const LazyComponent = lazy(() => import(`@views/${path}`))
return (
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
)
}
// 路由配置
const routes: RouteObject[] = [
{
path: '/',
element: lazyLoad('Home'))),
children: [
{
path: 'settings',
element: lazyLoad('Settings')
},
{
path: 'user',
element: lazyLoad('User'),
children: []
}
]
},
{ path: '*', element: lazyLoad('404') }
]
export default routes
三、使用useRoutes生成路由对象,并在App.tsx引入
import { useRoutes } from 'react-router-dom'
import routes from '@/router'
const App = () => {
// 通过useRoutes能够将我们创建的路由表映射成为路由对象
const elements = useRoutes(routes)
return elements
}
export default App
四、路由守卫
日常开发中,对于一些管理后台或者有角色区分的C端项目,需要控制不同角色的用户显示不同的内容、限制路由的访问、菜单的不同显示等,因此开发的时候都会设计一套系统权限。对于前端而言,系统权限往往跟路由守卫有着密切关系。
首先针对登录,系统中可能某一些界面需要登录后才能访问,一些界面不登录也能访问。因此需要在路由配置中增加表示需要登录的字段来标识哪些界面需要登录,哪些不需要登录。
其次针对菜单,不同的角色用户需要看见不同的菜单。通常做法:前端保存着所有的路由配置,后端返回当前用户的角色的资源权限,前端角色的资源权限筛选生成路由配置,能够起到限制作用。
既然路由需要定义是否需要登录字段,定义哪些角色能访问,因此我们需要在路由里加个字段来存放我们自定义配置,扩展RouteObject
。
增加一个meta
字段承载路由自定义配置,auth表示
页面是否需要登录
// src/typings/routes.d.ts
import type { IndexRouteObject, NonIndexRouteObject } from 'react-router-dom'
interface CustomRouteFields {
meta?: {
auth?: boolean
}
}
type AppIndexRouteObject = IndexRouteObject & CustomRouteFields
type AppNonIndexRouteObject = Omit<NonIndexRouteObject, 'children'> &
CustomRouteFields & {
children?: (AppIndexRouteObject | AppNonIndexRouteObject)[]
}
export type RouteProps = AppIndexRouteObject | AppNonIndexRouteObject
改造上面定义的路由配置
// 路由配置
const routes: RouteProps[] = [
{ path: '/login', element: lazyLoad('Login') },
{
path: '/',
element: lazyLoad('Home'))),
children: [
{
path: 'settings',
meta: {
auth: true
},
element: lazyLoad('Settings')
},
{
path: 'user',
meta: {
auth: true
},
element: lazyLoad('User'),
children: []
}
]
},
{ path: '*', element: lazyLoad('404') }
]
export default routes
本次主要讲路由守卫-登录拦截。
界面需要登录 + 用户未登录,同时满足的时候才需要跳到登录界面,登录成功后,需要回到原来的界面。
渲染的时候,通过react-router-dom的matchRoutes获取当前匹配的路由数组
,判断是否需要登录以及是否登录,如果没有,就跳转到/login
,如果已经登录了,就返回需要渲染的children
,因此我们将这个逻辑封装成一个高阶组件RouterAuth
。
// src/routes/RouterAuth.tsx
export const RouterAuth: FC = ({ children }) => {
const isLogin = getLoginStatus()
const location = useLocation()
// 匹配当前层级路由树
const mathchs = matchRoutes(routes, location)
// matchs是返回的层级路由, 第一个元素为根路由 最后一个元素为当前路由
const isNeedLogin = mathchs?.some((item) => {
const route: RouteProps = item.route
// 没有配置meta字段直接返回
if (!route.meta) return false
// 返回是否需要登录
return route.meta.auth
})
if (isNeedLogin && !isLogin) {
// 跳转到登录页面,state保存原路由
return <Navigate to='/login' state={{ from: location.pathname }} replace />
}
return <>{children}</>
}
然后用这个组件去包裹需要渲染的元素,这样每次渲染一个路由的时候就会做拦截。
const App = () => {
const elements = useRoutes(routes)
return (
<>
<RouterAuth>{elements}</RouterAuth>
</>
)
}
到此,我们通过封装高阶组件RouterAuth实现了路由登录拦截。后面文章再继续介绍路由的权限管控。
总的来说,react对初学者确实比vue要求高些,react-router很多东西需要自己手动去封装和处理,不像vue-router有封装好的全局守卫的函数,对开发者还是非常友好的;但是不能说react就不友好,只能说各有各的好处。就这样子吧,累了。