继续上一篇:✋手摸手系列(一/1): vite react typescript reactHook mobx(非脚手架)(从0开始)(包含路由权限控制)
后台系列目录
-
✋手摸手系列(一/1): vite react typescript reactHook mobx(非脚手架)(从0开始)(包含路由权限控制)
-
✋手摸手系列(一/2): vite react typescript reactHook mobx(非脚手架)(从0开始):路由和权限搭建
-
✋手摸手系列(一/3): vite react typescript reactHook mobx(非脚手架)(从0开始):mobx 接入
参考资料 umi路由设计umijs.org/zh-CN/docs/…
项目地址 github.com/wowhoonet/v…
路由搭建
安装 react-route react-route-dom 依赖
yarn add react-router react-router-dom
yarn add @types/react-router @types/react-router-dom -D
在根目录创建 route>index.ts, 代码如下:
import React from "react";
/**
* 路由文件
*/
export interface IRoute {
path: string;
exact?: boolean;
component?: React.ComponentType<any>;
routes?: IRoute[];
wrappers?: React.ComponentType<any>[];
title?: string;
redirect?: string;
hide?: boolean;
icon?: any;
}
export const routes: IRoute [] = [
{
path: '/home',
exact: true,
title: 'home页面',
component: React.lazy(() => import("@/view/Home")),
}
]
新增入口文件 view->App->index.tsx 代码如下:
import { IRoute, routes } from '@/route';
import React, { Suspense, useMemo } from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import styles from './index.module.less';
export default function App (): React.ReactElement {
const getChildrenComponent = (
route: IRoute,
key: number,
pPath: string = ""
) => {
const path = pPath + route.path;
return route.redirect ? (
<Redirect key={key} to={route.redirect} from={route.path}></Redirect>
) : (
(route.component || route.routes?.length > 0) && (
<Route key={key} path={path} exact={route.exact}>
{route.wrappers?.length > 0
? route.wrappers.reduceRight(
(element: any, wrapper: any) =>
React.createElement(wrapper, {}, element),
React.createElement(
route.component || React.Fragment,
{},
<Switch>
{route?.routes?.map((croute, rindex) =>
getChildrenComponent(croute, rindex, path)
)}
</Switch>
)
)
: React.createElement(
route.component || React.Fragment,
{},
<Switch>
{route?.routes?.map((croute, rindex) =>
getChildrenComponent(croute, rindex, path)
)}
</Switch>
)}
</Route>
)
);
};
const Routes = useMemo(() => {
const _Routes = routes.map((route, rindex) =>
getChildrenComponent(route, rindex)
);
console.log(_Routes, "_Routes");
return _Routes;
}, []);
return <Suspense fallback={<div>加载中</div>}>
<HashRouter basename="/">
<Switch>{Routes}</Switch>
</HashRouter>
</Suspense>
}
这里需要注意的是我们的 routes 结构是个嵌套结构,所以我们需要抽出方法来实现递归,这里有个wrappers大家可以猜一下干甚用的,下面马上就要讲到了
这时候 yarn dev 跑一下, 然后访问http://localhost:3000/#/home 应该就可以看到效果了
权限控制
wrapper
译为:包装器
概念来源于 umi 的路由设计,小看了一下源码,然后加入到咱们项目中来
开整 wrapper 设计
- 新建 wrappers->auth.tsx
import React from 'react';
import { Redirect } from 'react-router';
import styles from './index.module.less';
export default function Auth (props: {
children: React.ReactElement
}): React.ReactElement {
const isLogin = false; // 后续换成自己的 token 校验
const {children, ...cprops} = props;
if(isLogin) {
return children
}
return <Redirect to="/login"></Redirect>;
}
大家看到这里应该知道我的用意了吧,意思是如果我登录了我就渲染我的 children,如果没有登录就重定向到 login;结合一下 route 里面的wraper代码
// xxx
route.wrappers.reduceRight(
(element: any, wrapper: any) =>
React.createElement(wrapper, {}, element),
React.createElement(
route.component || React.Fragment,
{},
<Switch>
{route?.routes?.map((croute, rindex) =>
getChildrenComponent(croute, rindex, path)
)}
</Switch>
)
)
// xxx
用 wraper 把要渲染的路由包裹起来,就起到了高阶组件的用意了,也就是拦截器的作用
现在完善一下 view->Login 路由组件和 route->index.ts 的路由内容
Run!run 跑起来
在地址栏输入 http://localhost:3000/#/home 会自动重定向到 login
总结
- 引入 react-router 系列包
- 通过递归方式生成组件,原理是通过 React.createElement生成包裹组件
- 通过wrapper 的方式进行路由拦截