arlyxiao / best-practice

1 stars 0 forks source link

vite + ant design/pro 踩坑记录 #89

Open arlyxiao opened 2 years ago

arlyxiao commented 2 years ago

对于大项目,加载的组件比较多的话,build 时可能会碰到内存不够情况,可以用

export NODE_OPTIONS='--max-old-space-size=7168'

vite.config.ts 配置

import react from '@vitejs/plugin-react';
import { join } from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    sourcemap: false,
    commonjsOptions: {
      sourceMap: false,
    },
    minify: true,
  },
  resolve: {
    alias: {
      '@': join(__dirname, 'src'),
      '~antd': join(__dirname, 'node_modules/antd'),
    },
  },
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
        modifyVars: {
          'root-entry-name': 'default',
        },
      },
    },
  },
  plugins: [react()],
});

tsconfig.json 配置

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    },
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

tsconfig.node.json

{
  "compilerOptions": {
    "composite": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

global.less

@import '~antd/es/style/themes/default.less';
@import '~antd/dist/antd.less';

html,
body,
#root {
  height: 100%;
}

.colorWeak {
  filter: invert(80%);
}

.ant-layout {
  min-height: 100vh;
}

.ant-pro-fixed-header-action {
  display: block;
}

.ant-pro-sider-logo h1 {
  font-size: 14px;
  height: unset;
  margin: unset;
}

.ant-form-item {
  margin: 0 0 35px !important;
}

canvas {
  display: block;
}

body {
  text-rendering: optimizelegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

ul,
ol {
  list-style: none;
}

@media (max-width: @screen-xs) {
  .ant-table {
    width: 100%;
    overflow-x: auto;

    &-thead > tr,
    &-tbody > tr {
      > th,
      > td {
        white-space: pre;

        > span {
          display: block;
        }
      }
    }
  }
}

@media only screen and (max-width: 800px) {
  .ant-pro-global-header {
    h1 {
      display: none !important;
    }

    img {
      width: 30px;
    }
  }
}

// IE11
@media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) {
  body .ant-design-pro > .ant-layout {
    min-height: 100vh;
  }
}

路由设置

import React, { Suspense } from 'react';

import MainLayout from '@/layouts/MainLayout';
import { Loading } from '@/pages/Loading';

import Login from '@/pages/User/Login';
import Welcome from '@/pages/Welcome';
import { Route, Routes } from 'react-router-dom';

import './global.less';

const UserAuthDev = React.lazy(() => import('@/pages/user-auth/UserAuthDev'));
export default function App() {
  return (
    <>
      <Routes>
        <Route path="/user/login" element={<Login />} />

        <Route path="/" element={<MainLayout />}>
          <Route index element={<Welcome />} />

          <Route
            path="user-auth/dev"
            element={
              <Suspense fallback={<Loading />}>
                <UserAuthDev />
              </Suspense>
            }
          />
      </Routes>
    </>
  );
}

layout 配置

import Footer from '@/components/Footer';
import HeaderRight from '@/components/HeaderRight';
import {
  FileSearchOutlined,
  SettingOutlined,
  TableOutlined,
  TeamOutlined,
  UserOutlined,
} from '@ant-design/icons';
import { Loading } from '@/pages/loading';
import type { MenuDataItem } from '@ant-design/pro-components';
import { ProLayout } from '@ant-design/pro-components';
import { useEffect, useState } from 'react';
import { Link, Outlet, useLocation } from 'react-router-dom';
import { routes } from '../../config/router';
import logo from '../assets/logo.svg';
import styles from './styles.module.css';

const menuIcons: any = {
  user: <UserOutlined />,
  search: <FileSearchOutlined />,
  table: <TableOutlined />,
  setting: <SettingOutlined />,
  team: <TeamOutlined />,
};

const loopMenuItem = (menus: MenuDataItem[]): MenuDataItem[] =>
  menus.map(({ icon, routes, ...item }) => ({
    ...item,
    icon: icon && menuIcons[icon],
    routes: routes && loopMenuItem(routes),
  }));

export default () => {
  const [currentLocation, setCurrentLocation] = useState(
    window.location.pathname,
  );
  const location = useLocation();

  useEffect(() => {
    setCurrentLocation(location.pathname);
  }, [location]);

  return (
    <>
      <ProLayout
        menuHeaderRender={() => (
          <a href="/">
            <img className={styles.logo} src={logo} />
            <h1 className={styles.caption}>your title</h1>
          </a>
        )}
        rightContentRender={() => {
          return <HeaderRight />;
        }}
        footerRender={() => <Footer />}
        menuItemRender={(item, dom) => {
          return <Link to={item.path ?? '/'}>{dom}</Link>;
        }}
        location={{
          pathname: currentLocation,
        }}
        fixSiderbar
        menu={{ request: async () => loopMenuItem(routes) }}
      >
        <Outlet />
      </ProLayout>
    </>
  );
};

Entry file

import { ConfigProvider } from 'antd';
import enUS from 'antd/es/locale/en_US';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';

import App from './lite';

const container = document.getElementById('root');
const root = createRoot(container!);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <ConfigProvider locale={enUS}>
        {' '}
        <App />
      </ConfigProvider>
    </BrowserRouter>
  </React.StrictMode>,
);