umi4js集成Material UI

本文介绍了如何使用umi4js创建项目并集成MaterialUI,包括启用dva状态管理,利用useDispatch和useSelector进行数据操作,以及根据routes动态展示菜单。同时,文章展示了如何编辑布局,使用createTheme创建主题,并实现light/dark模式切换。
摘要由CSDN通过智能技术生成

效果图

在这里插入图片描述

创建umi项目

一直在用ant design pro做项目,着实有点儿审美疲劳了,正好最近有个小项目尝试下Material UI,发现Material UI也挺好用的,在此做个总结。

创建umi项目

# 官方推荐使用pnpm构建项目

pnpm dlx create-umi@latest

umi 目录结构

.
├── config
│   └── config.ts
├── dist
├── mock
│   └── app.ts|tsx
├── src
│   ├── .umi
│   ├── .umi-production
│   ├── layouts
│   │   ├── BasicLayout.tsx
│   │   ├── index.less
│   ├── models
│   │   ├── global.ts
│   │   └── index.ts
│   ├── pages
│   │   ├── index.less
│   │   └── index.tsx
│   ├── utils // 推荐目录
│   │   └── index.ts
│   ├── services // 推荐目录
│   │   └── api.ts
│   ├── app.(ts|tsx)
│   ├── global.ts
│   ├── global.(css|less|sass|scss)
│   ├── overrides.(css|less|sass|scss)
│   ├── favicon.(ico|gif|png|jpg|jpeg|svg|avif|webp)
│   └── loading.(tsx|jsx)
├── node_modules
│   └── .cache
│       ├── bundler-webpack
│       ├── mfsu
│       └── mfsu-deps
├── .env
├── plugin.ts 
├── .umirc.ts // 与 config/config 文件 2 选一
├── package.json
├── tsconfig.json
└── typings.d.ts

启用插件

# 安装插件
pnpm add -D @umijs/plugins
// config/config.ts
export default defineConfig({
  //...
  plugins: [
    "@umijs/plugins/dist/tailwindcss.js",
    "@umijs/plugins/dist/dva",
    '@umijs/plugins/dist/initial-state',
    '@umijs/plugins/dist/model',
  ],
  // mock: false,
  tailwindcss: {},
  dva: {},
  initialState: {},
  model: {},
});

dva

// src/model/layout.ts
export interface LayoutModelState {
  opened: boolean;
  isOpen: Array<string>;
}

export default {
  namespace: 'layout',
  state: {
    opened: true,
    isOpen: [],
  },
  reducers: {
    openLeftDrawer(state: LayoutModelState) {
      return {
        ...state,
        opened: state.opened,
      };
    },
    openMenu(state: { isOpen: Array<string> }, { payload }: { payload: any }) {
      const { menuId } = payload;
      return {
        ...state,
        isOpen: [ menuId ]
      };
    },
  },
};

页面中使用dva

const mapStateToProps = (state: any) => {
  return {
    layout: state.layout,
    loading: state.loading,
  };
};

const Layout: React.FC = (props: any) => {
  // 省略代码
  return (
    <>
      <Main theme={themes} open={props.layout.opened}>
    </>
  )

};
export default connect(mapStateToProps)(Layout);

使用useDispatch, useSelector

const Layout: React.FC = (props: any) => {
  const dispatch = useDispatch();
  const layoutModel: LayoutModelState = useSelector((state: any) => {
   return state.layout;
 });
  // 省略代码
  return (
    <>
      <Main theme={themes} open={layoutModel.opened}>
    </>
  )
};
export default Layout;

编辑layout

...

<ColorModeContext.Provider value={colorMode}>
      <StyledEngineProvider injectFirst>
        <ThemeProvider theme={themes}>
          <CssBaseline />
          <Box sx={{ display: 'flex' }}>
            {/* header */}
            <AppBar
              enableColorOnDark
              position="fixed"
              color="inherit"
              elevation={0}
            >
              <Toolbar>
                <Header />
              </Toolbar>
            </AppBar>
            {/* drawer */}
            <Sidebar />
            {/* main content */}
            <Main theme={themes} open={props.layout.opened}>
              {/* breadcrumb */}
              <Outlet />
            </Main>
            <Customization />
          </Box>
        </ThemeProvider>
      </StyledEngineProvider>
    </ColorModeContext.Provider>

ColorModeContext: 用于切换light/dark模式。

createTheme

const themes = React.useMemo(
    () =>
      createTheme({
        palette: {
          mode,
          ...(mode === 'light'
            ? {
                primary: {
                  light: colors.primaryLight,
                  main: colors.primaryMain,
                  200: colors.primary200,
                },
                secondary: {
                  light: colors.secondaryLight,
                  main: colors.secondaryMain,
                },
              }
            : {
                primary: {
                  light: colors.darkPrimaryLight,
                  main: colors.darkPrimaryMain,
                  200: colors.darkPrimary200,
                },
                secondary: {
                  light: colors.darkSecondaryLight,
                  main: colors.darkPrimaryMain,
                },
              }),
        },
      }),
    [mode],
  );

改变mode实现light/dark的切换

根据routes展示菜单

// routes
[
  {
    id: '1',
    icon: 'dashboard',
    title: 'Dashboard',
    type: 'group',
    caption: 'Dashboard Caption',
    routes: [
      {
        id: '2',
        path: '/dashboard',
        icon: 'dashboard',
        type: 'collapse',
        title: 'Dashboard',
        routes: [
          {
            id: '3',
            path: '/dashboard/one',
            type: 'item',
            title: '测试菜单',
            component: 'index'
          },
          {
            id: '4',
            path: '/dashboard/two',
            type: 'item',
            title: '测试菜单2',
            component: 'docs'
          }
        ]
      },
      {
        id: '5',
        path: '/dashboard/two',
        type: 'item',
        title: '测试菜单2',
        component: 'docs'
      }
    ],
  },
]

type=“group”: 菜单分组。
type=“collapse”: 菜单目录。
type=“item”: 菜单。

服务端返回routes

// app.tsx

let extraRoutes: any[];

export function patchClientRoutes({ routes }: {routes: any}) {
  routes[0].children = [];
  forEachRouter(routes[0], extraRoutes);
}

function forEachRouter(currentRoute: any, routes: any) {
  routes.forEach((route: any) => {
    if(!!route.routes && route.routes.length > 0) {
      forEachRouter(currentRoute, route.routes);
    }
    if(route.type === 'item') {
      const tempComponent = Loadable(lazy(() => import(`@/pages/${route.component}`)));
      currentRoute.children.unshift({
        path: route.path,
        element: createElement(tempComponent)
      });
    }
  });
}

export async function render(oldRender: () => void) {
    const res = await fetch("/api/sys/get/routes");
    const data = await res.json()
    extraRoutes = data.data;
    oldRender();
}

patchClientRoutes: 该函数直接修改routes,实现组件动态注册。
Loadable: 进度条,类似于nprogress.js。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值