✋手摸手系列(一/2):react后台系统(从0开始)

4/26/2021 vitereact

# 第二篇

# 路由和权限搭建

# 继续上一篇:✋手摸手系列(一/1): vite react typescript reactHook mobx(非脚手架)(从0开始)(包含路由权限控制) (opens new window)

# 后台系列目录

  1. # ✋手摸手系列(一/1): vite react typescript reactHook mobx(非脚手架)(从0开始)(包含路由权限控制) (opens new window)

  2. # ✋手摸手系列(一/2): vite react typescript reactHook mobx(非脚手架)(从0开始):路由和权限搭建 (opens new window)


# 参考资料 umi路由设计https://umijs.org/zh-CN/docs/routing#页面跳转

# 项目地址 https://github.com/wowhoonet/vite-react-mobx-admin

# 路由搭建

# 安装 react-route react-route-dom 依赖

yarn add react-router react-router-dom
yarn add @types/react-router @types/react-router-dom -D
1
2

# 在根目录创建 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")),
  }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 新增入口文件 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>
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

这里需要注意的是我们的 routes 结构是个嵌套结构,所以我们需要抽出方法来实现递归,这里有个wrappers大家可以猜一下干甚用的,下面马上就要讲到了

# 这时候 yarn dev 跑一下, 然后访问http://localhost:3000/#/home 应该就可以看到效果了

# 权限控制

# wrapper

译为:包装器

概念来源于 umi 的路由设计,小看了一下源码,然后加入到咱们项目中来

# 开整 wrapper 设计

  1. 新建 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>;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

大家看到这里应该知道我的用意了吧,意思是如果我登录了我就渲染我的 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

用 wraper 把要渲染的路由包裹起来,就起到了高阶组件的用意了,也就是拦截器的作用

现在完善一下 view->Login 路由组件和 route->index.ts 的路由内容

image.png

# Run!run 跑起来

在地址栏输入 http://localhost:3000/#/home 会自动重定向到 login

# 总结

  1. 引入 react-router 系列包
  2. 通过递归方式生成组件,原理是通过 React.createElement生成包裹组件
  3. 通过wrapper 的方式进行路由拦截

# 下一篇讲 mobx,大家一起加油

Last Updated: 4/25/2021, 10:24:18 AM