百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文
React 19 + React-Router v7 超级详细实用、好理解的优雅动态路由懒加

React 19 + React-Router v7 超级详细实用、好理解的优雅动态路由懒加

  • 网站名称:React 19 + React-Router v7 超级详细实用、好理解的优雅动态路由懒加
  • 网站分类:技术文章
  • 收录时间:2025-09-14 16:09
  • 网站地址:

进入网站

“React 19 + React-Router v7 超级详细实用、好理解的优雅动态路由懒加” 网站介绍

  • 本文仅面向前端用户,其他端请绕道。
  • 项目选型了 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
})
}
  • 最终效果


具体可以将项目拉下来体验下,感谢各位的阅读,也欢迎各位评论区留言指点不足之处。