# 按需加载路由
# 官方的懒加载
# 个人实现
下面自己实现的异步按需加载组件的方式,之前没找到官方的懒加载。 建议大家还是使用官方的,下面的当做学习,提供思路。
# 第一步:创建一个异步组件
//创建文件src/common/AsyncComponent.js
import React, { Component } from "react";
export default function asyncComponent(importComponent) {
class AsyncComponent extends Component {
constructor(props) {
super(props);
this.state = {
component: null
};
}
async componentDidMount() {
const { default: component } = await importComponent();
this.setState({
component: component
});
}
render() {
const C = this.state.component;
return C ? <C {...this.props} /> : null;
}
}
return AsyncComponent;
}
# 第二步:使用异步组件:我们将使用asyncComponent动态导入我们想要的组件。
例如:
const AsyncHome = asyncComponent(() => import("./containers/Home"));
下面来个项目中的路由例子
// route.ts
import { RouteProps } from 'react-router-dom'
import asyncComponent from '@/common/js/async-component' // 创建异步组件,实现路由按需加载
import App from '@/app'
import PermissionRoute from '@/components/permission-route/permission-route'
import { traverseTree } from '@/utils/utils'
import IndexPage from '@/pages/index'
// 大厅
const Market = asyncComponent(() => import('@/pages/market/market'))
// 我是买家
const Buyer = asyncComponent(() => import('@/pages/buyer/buyer'))
// 我是卖家
const Seller = asyncComponent(() => import('@/pages/seller/seller'))
// 核户申请页面
const CheckAccount = asyncComponent(() => import('@/pages/check-account/check-account'))
// 订单详情页
const OrderDetail = asyncComponent(() => import('@/pages/order-detail/order-detail'))
// 测试页面
const Test = asyncComponent(() => import('@/pages/test/test'))
// 图标预览页面
const TestIcon = asyncComponent(() => import('@/pages/test-icon/test-icon'))
// 发布票据页面
const IssueDraft = asyncComponent(() => import('@/pages/issue-draft/issue-draft'))
export interface MenuRoute {
/** 标题 */
title?: string,
/** 是否要求登录 */
requireLogin?: boolean,
/** 是否不在菜单中显示 */
hideInMenu?: boolean,
/** 路由参数 */
routeProps: RouteProps,
/** 子菜单 */
children?: MenuRoute[],
/** 菜单图标 */
icon?: string,
/** 父级菜单对象 */
parent?: MenuRoute | null,
/** 完整路径 */
fullPath?: string,
}
export const routes: MenuRoute[] = [
{
routeProps: {
path: '/',
element: <App />,
},
children: [
{
routeProps: {
index: true,
element: <IndexPage />
},
title: '首页',
hideInMenu: true
},
{
routeProps: {
path: 'market',
element: <Market />
},
title: '大厅',
},
{
routeProps: {
path: 'buyer',
element: <Buyer />
},
requireLogin: true,
title: '我是买家',
},
{
routeProps: {
path: 'seller',
element: <Seller />
},
requireLogin: true,
title: '我是卖家',
},
{
routeProps: {
path: 'issue',
element: <IssueDraft />
},
requireLogin: true,
title: '发布票据',
},
{
routeProps: {
path: 'checkAccount',
element: <CheckAccount />
},
requireLogin: true,
title: '核户申请',
},
{
routeProps: {
path: '*',
element: <IndexPage />
},
hideInMenu: true
},
{
routeProps: {
path: 'orderDetail',
element: <OrderDetail />
},
requireLogin: true,
},
]
}
]
if (process.env.NODE_ENV === 'development') {
routes[0].children?.push(...[
{
routeProps: {
path: 'test',
element: <Test />
},
title: '测试页面',
hideInMenu: true
},
{
routeProps: {
path: 'test-icon',
element: <TestIcon />
},
title: '图标预览页面',
hideInMenu: true
}
])
}
// 打平的路由数组
export const flattenRoutes: MenuRoute[] = []
// 完整路径-路由对象 map
const routesMap: Record<string, MenuRoute> = {}
// 递归路由,添加页面权限控制和父节点、完整路径
traverseTree(routes, (route, parent) => {
if (route.requireLogin && route.routeProps.element) {
route.routeProps.element = <PermissionRoute route={route}>{route.routeProps.element}</PermissionRoute>
}
route.parent = parent
// 完整路径
let fullPath: string | undefined
if (parent) {
fullPath = route.routeProps.index ? parent.fullPath : `${(parent.fullPath || '').replace(/\/$/, '')}/${route.routeProps.path || ''}`
} else {
fullPath = route.routeProps.path
}
route.fullPath = fullPath
flattenRoutes.push(route)
if (fullPath) {
routesMap[fullPath] = route
}
})
// 获取某个路由对象
export const getRoute: (fullPath: string) => MenuRoute | null = function getRoute(fullPath) {
return routesMap[fullPath] || null
}
export default routes
// index.ts
import React from 'react'
import ReactDOM from 'react-dom/client'
import {
BrowserRouter,
Routes,
Route,
} from 'react-router-dom'
import { ConfigProvider } from 'antd'
import { Provider } from 'react-redux'
import { store } from './store/index'
// ant design 样式重置
import 'antd/dist/antd.less'
import './styles/antd-reset.scss'
// 页面重置样式
import './styles/reset.scss'
// 全局样式
import './styles/global.scss'
// 由于 antd 组件的默认文案是英文,所以需要修改为中文
import zhCN from 'antd/es/locale/zh_CN'
import 'moment/locale/zh-cn'
import { routes, MenuRoute } from '@/router'
import '@/utils/icon' // 引入项目里本地图标
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
)
// 渲染路由组件
const renderRoute = (route: MenuRoute) => {
const children = route.children?.map(childMenu => renderRoute(childMenu))
return <Route key={route.fullPath || '/'} {...route.routeProps}>{children}</Route>
}
root.render(
<ConfigProvider locale={zhCN} autoInsertSpaceInButton={false}>
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<Routes>
{
routes.map(renderRoute)
}
</Routes>
</BrowserRouter>
</Provider>
</React.StrictMode>
</ConfigProvider>
)
参考文章: