2025-02-10 10:01:47 更新最新代码
前言
- 项目地址:github.com/smithyj/exp…
- 本文仅面向前端用户,其他端请绕道。
- 项目选型了
Rspack
、React 19
、React Router v7
作为项目基础。 - 按
Rspack
官方教程搭建下来,顺利启动项目,并按React Router v7
官方文档简单集成。
问题
- 集成进来的都很丑,而且做懒加载实现方式不够优雅,而且网上也都是如下图一般的教程。
- 这个还行,比其他的好多了,但是还不够优雅
- 其他的都如下图一般,他们写的难道不膈应吗?(这个世界上就没有喜欢优雅的吗?)
过程
-
发现了问题,那么我们就来想办法来解决,给自己先提一个需求
- 1、可配置化
- 2、最多配置一个地方
- 3、支持
middlewares
- 这里可以喊后端同学看看,这个概念在前端里面可以叫做路由守卫
- 实现了这个,基本上其他的都很好实现了
-
第一步,我们在
index.tsx
引入App
import ReactDOM from 'react-dom/client'
import App from './App'
const rootEl = document.getElementById('root')
if (rootEl) {
const root = ReactDOM.createRoot(rootEl)
root.render(<App />)
}
- 第二步,我们在
App.tsx
中引入RouterProvider
import { ConfigProvider, App as AppContainer } from 'antd'
import zhCN from 'antd/locale/zh_CN'
import './App.less'
import { RouterProvider } from 'react-router'
import { router } from './router'
const App = () => {
return (
<ConfigProvider locale={zhCN}>
<AppContainer component={false}>
<RouterProvider router={router} />
</AppContainer>
</ConfigProvider>
)
}
export default App
- 第三步,我们先实现懒加载组件
components/LazyImport
import { ComponentType, FC, LazyExoticComponent, Suspense } from 'react'
import LazyLoading from '@/components/LazyLoading'
interface LazyImportProps {
lazy?: LazyExoticComponent<ComponentType>
}
export const LazyImport: FC<LazyImportProps> = ({ lazy }) => {
const Component = lazy ? lazy : () => null
return (
<Suspense fallback={<LazyLoading />}>
<Component />
</Suspense>
)
}
- 第四步,我们先实现懒加载 Loading 组件
components/LazyLoading
import { Col, Row, Spin } from 'antd'
const LazyLoading = () => {
return (
<Row align="middle" justify="center" style={{ minHeight: '100%' }}>
<Col>
<Spin spinning />
</Col>
</Row>
)
}
export default LazyLoading
- 第五步,我们来实现下
router/index.tsx
- 这里细心点看,应该可以发现爆改了下 element 属性类型
- 然后导出了个
router
变量给RouterProvider
,主要是为了以后可以在非页面中使用路由能力
import { lazy } from 'react'
import { buildRoutes, RouteConfig } from './utils'
import { createBrowserRouter } from 'react-router'
import ErrorBoundary from '@/components/ErrorBoundary'
const routeConfig: RouteConfig[] = [
{
ErrorBoundary: ErrorBoundary,
children: [
{
path: '/login',
element: lazy(() => import('@/pages/Login')),
},
{
// 应用基础布局
element: lazy(() => import('@/layouts/BasicLayout')),
middlewares: [
// 管理员登录验证中间件
lazy(() => import('@/middlewares/AdminAuthMiddleware')),
],
children: [
{
path: '/',
index: true,
element: lazy(() => import('@/pages/Home')),
},
{
// 谷歌验证
path: '/google-2fa',
element: lazy(() => import('@/pages/Google2FA')),
},
{
middlewares: [
// 谷歌验证中间件
lazy(() => import('@/middlewares/AdminGoogle2FAMiddleware')),
// 页面权限验证中间件
lazy(() => import('@/middlewares/AdminPagePermissionMiddleware')),
],
children: [
{
// 解绑谷歌验证
path: '/unbind-google-2fa',
element: lazy(() => import('@/pages/UnbindGoogle2FA')),
},
{
// 修改密码
path: '/change-password',
element: lazy(() => import('@/pages/ChangePassword')),
},
{
// 仪表盘
path: '/dashboard',
element: lazy(() => import('@/pages/Dashboard')),
},
{
// 系统设置
path: '/setting',
element: lazy(() => import('@/pages/Setting')),
},
{
// 管理员管理
path: '/admin',
element: lazy(() => import('@/pages/Admin')),
},
{
// 角色管理
path: '/admin/role',
element: lazy(() => import('@/pages/AdminRole')),
},
{
// 权限管理
path: '/admin/permission',
element: lazy(() => import('@/pages/AdminPermission')),
},
],
},
],
},
],
},
]
export const routes = buildRoutes(routeConfig)
export const router = createBrowserRouter(routes)
- 第六步,为了实现我们的第五步功能,我们来实现下
router/utils.tsx
- 重点就是这个文件啦
- 为了更好的实现和IDE智能提示(最讨厌IDE爆红了)
- 我们自定义了一个
RouteConfig
来重新实现路由配置,直接继承自 RouteObject,并且为了配置组件统一性,减除了不必要的属性(lazy,Component,children,element)
- 我们自定义了
element
、middlewares
、children
属性
import { Outlet, RouteObject } from 'react-router'
import { ComponentType, LazyExoticComponent } from 'react'
import { LazyImport } from '@/components/LazyImport'
export type LazyComponent = LazyExoticComponent<ComponentType>
export type RouteConfig = Omit<
RouteObject,
'element' | 'children' | 'Component' | 'lazy'
> & {
element?: LazyComponent
middlewares?: LazyComponent[]
children?: RouteConfig[]
}
export const buildRoutes = (routes: RouteConfig[]): RouteObject[] => {
return routes.map((item) => {
const { element, middlewares, children, ...restProps } = item
// 要返回的路由对象
let routeObject: RouteObject = {
...restProps,
}
// 递归构建子路由
if (children) {
routeObject.children = buildRoutes(children)
}
// 异步加载组件
routeObject.element = element ? <LazyImport lazy={element} /> : undefined
// 中间件处理
if (middlewares && middlewares.length > 0) {
// 从后往前遍历中间件,这样中间件的执行顺序就是从前往后
// 例如:[A, B, C] => A(B(C()))
for (let i = middlewares.length - 1; i >= 0; i--) {
const middleware = middlewares[i]
routeObject = {
element: <LazyImport lazy={middleware} />,
children: [routeObject],
}
}
} else {
// 如果没有中间件,也没有 element 则传入 <Outlet />
routeObject.element = routeObject.element ?? <Outlet />
}
// 返回路由对象
return routeObject
})
}
- 最终效果
具体可以将项目拉下来体验下,感谢各位的阅读,也欢迎各位评论区留言指点不足之处。