React18+pnpm+Ts+React-Router v6从0-1搭建后台系统

大家好,我是鱼樱!!!

关注公众号【鱼樱AI实验室】持续每天分享更多前端和AI辅助前端编码新知识~~喜欢的就一起学反正开源至上,无所谓被诋毁被喷被质疑文章没有价值~

一个城市淘汰的自由职业-农村前端程序员(虽然不靠代码挣钱,写文章就是为爱发电),兼职远程上班目前!!!热心坚持分享~~~

今天大家分享一个0-1搭建React后台管理系统的最佳实践模板思路~ 并且推荐一个双越老师的划水AI项目 有需要的可以私我走优惠通道~

React18/19 + pnpm + TypeScript + React-Router v6 后台管理系统最佳实践

项目基础搭建

技术栈选择

  • React 18/19:使用最新的React版本获取性能提升和新特性
  • TypeScript:提供类型安全,提升代码质量和开发体验
  • pnpm:比npm/yarn更快的包管理器,节省磁盘空间
  • React Router v6:最新版路由,支持嵌套路由和数据加载
  • Vite:比Create React App更快的构建工具

项目初始化

bash 复制代码
# 使用pnpm创建Vite+React+TS项目
pnpm create vite my-admin --template react-ts

# 进入项目目录
cd my-admin

# 安装依赖
pnpm install

# 安装React Router
pnpm add react-router-dom@6

项目目录结构

bash 复制代码
src/
├── assets/          # 静态资源
├── components/      # 公共组件
├── hooks/           # 自定义hooks
├── layouts/         # 布局组件
├── pages/           # 页面组件
├── services/        # API请求
├── stores/          # 状态管理
├── types/           # 类型定义
├── utils/           # 工具函数
├── App.tsx          # 应用入口
└── main.tsx         # 渲染入口

代码规范与工程化

ESLint 和 Prettier 配置

bash 复制代码
# 安装ESLint和Prettier
pnpm add -D eslint prettier eslint-plugin-react eslint-plugin-react-hooks
pnpm add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser
pnpm add -D eslint-config-prettier eslint-plugin-prettier

.eslintrc.js

javascript 复制代码
module.exports = {
  parser: '@typescript-eslint/parser',
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
    'plugin:prettier/recommended'
  ],
  plugins: ['react', '@typescript-eslint', 'prettier'],
  rules: {
    // 自定义规则
    'react/react-in-jsx-scope': 'off',
    'prettier/prettier': 'error'
  }
};

.prettierrc

json 复制代码
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "printWidth": 100,
  "trailingComma": "es5"
}

Git 提交规范

bash 复制代码
# 安装commitlint和husky
pnpm add -D @commitlint/cli @commitlint/config-conventional husky lint-staged

commitlint.config.js

javascript 复制代码
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert']
    ]
  }
};

设置husky

bash 复制代码
# 初始化husky
npx husky install

# 添加pre-commit钩子
npx husky add .husky/pre-commit "npx lint-staged"

# 添加commit-msg钩子
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"

配置lint-staged

json 复制代码
// package.json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

编辑器规范

.editorconfig

ini 复制代码
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

VSCode设置

创建.vscode/settings.json

json 复制代码
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "typescript.tsdk": "node_modules/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true
}

路由与权限设计

React Router v6 配置

tsx 复制代码
// src/routes/index.tsx
import { lazy, Suspense } from 'react';
import { Navigate, RouteObject } from 'react-router-dom';
import MainLayout from '@/layouts/MainLayout';
import AuthGuard from '@/components/AuthGuard';
import Loading from '@/components/Loading';

// 懒加载页面
const Dashboard = lazy(() => import('@/pages/Dashboard'));
const UserList = lazy(() => import('@/pages/User/List'));
const UserDetail = lazy(() => import('@/pages/User/Detail'));
const Login = lazy(() => import('@/pages/Login'));

// 路由配置
export const routes: RouteObject[] = [
  {
    path: '/',
    element: <MainLayout />,
    children: [
      { index: true, element: <Navigate to="/dashboard" replace /> },
      {
        path: 'dashboard',
        element: (
          <AuthGuard requiredPermissions={['dashboard:view']}>
            <Suspense fallback={<Loading />}>
              <Dashboard />
            </Suspense>
          </AuthGuard>
        )
      },
      {
        path: 'users',
        children: [
          {
            index: true,
            element: (
              <AuthGuard requiredPermissions={['user:list']}>
                <Suspense fallback={<Loading />}>
                  <UserList />
                </Suspense>
              </AuthGuard>
            )
          },
          {
            path: ':id',
            element: (
              <AuthGuard requiredPermissions={['user:detail']}>
                <Suspense fallback={<Loading />}>
                  <UserDetail />
                </Suspense>
              </AuthGuard>
            )
          }
        ]
      }
    ]
  },
  {
    path: '/login',
    element: (
      <Suspense fallback={<Loading />}>
        <Login />
      </Suspense>
    )
  },
  { path: '*', element: <Navigate to="/login" replace /> }
];

权限设计

基于RBAC的权限设计

tsx 复制代码
// src/components/AuthGuard.tsx
import { ReactNode } from 'react';
import { Navigate } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth';

interface AuthGuardProps {
  children: ReactNode;
  requiredPermissions?: string[];
}

const AuthGuard = ({ children, requiredPermissions = [] }: AuthGuardProps) => {
  const { isAuthenticated, hasPermissions } = useAuth();

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  if (requiredPermissions.length > 0 && !hasPermissions(requiredPermissions)) {
    return <Navigate to="/403" replace />;
  }

  return <>{children}</>;
};

export default AuthGuard;

权限相关的Hook

tsx 复制代码
// src/hooks/useAuth.ts
import { useCallback } from 'react';
import { useAuthStore } from '@/stores/authStore';

export const useAuth = () => {
  const { user, permissions, setUser, logout } = useAuthStore();

  const isAuthenticated = !!user;

  const hasPermissions = useCallback(
    (requiredPermissions: string[]) => {
      if (!permissions || permissions.length === 0) return false;
      return requiredPermissions.every(permission => permissions.includes(permission));
    },
    [permissions]
  );

  return {
    user,
    isAuthenticated,
    permissions,
    hasPermissions,
    setUser,
    logout
  };
};

状态管理:Zustand vs Redux

比较

特性 Zustand Redux (Redux Toolkit)
上手难度 简单 中等
样板代码 中等 (RTK减少了大量样板代码)
体积 轻量 较重
中间件支持 有,但不太丰富 丰富
开发工具 有基础支持 强大
异步处理 直接支持 需要RTK Query或thunk
社区生态 成长中 成熟

选择建议

  • Zustand:适合中小型项目,需要快速开发,简单状态管理
  • Redux:适合大型项目,有复杂状态逻辑,需要强大的调试工具和中间件支持

Zustand最佳实践

typescript 复制代码
// src/stores/authStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';

interface User {
  id: string;
  name: string;
}

interface AuthState {
  user: User | null;
  permissions: string[];
  setUser: (user: User, permissions: string[]) => void;
  logout: () => void;
}

export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({
      user: null,
      permissions: [],
      setUser: (user, permissions) => set({ user, permissions }),
      logout: () => set({ user: null, permissions: [] })
    }),
    {
      name: 'auth-storage'
    }
  )
);
typescript 复制代码
// src/stores/userStore.ts
import { create } from 'zustand';
import { fetchUsers } from '@/services/userService';

interface UserState {
  users: any[];
  loading: boolean;
  error: string | null;
  fetchUsers: () => Promise<void>;
}

export const useUserStore = create<UserState>((set) => ({
  users: [],
  loading: false,
  error: null,
  fetchUsers: async () => {
    set({ loading: true, error: null });
    try {
      const data = await fetchUsers();
      set({ users: data, loading: false });
    } catch (error) {
      set({ error: (error as Error).message, loading: false });
    }
  }
}));

Redux Toolkit最佳实践

typescript 复制代码
// src/stores/slices/authSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';

interface User {
  id: string;
  name: string;
}

interface AuthState {
  user: User | null;
  permissions: string[];
}

const initialState: AuthState = {
  user: null,
  permissions: []
};

export const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    setUser: (state, action: PayloadAction<{ user: User; permissions: string[] }>) => {
      state.user = action.payload.user;
      state.permissions = action.payload.permissions;
    },
    logout: (state) => {
      state.user = null;
      state.permissions = [];
    }
  }
});

export const { setUser, logout } = authSlice.actions;
export default authSlice.reducer;
typescript 复制代码
// src/stores/slices/userSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { fetchUsers } from '@/services/userService';

export const fetchUsersThunk = createAsyncThunk('users/fetchUsers', async () => {
  const response = await fetchUsers();
  return response;
});

const userSlice = createSlice({
  name: 'users',
  initialState: {
    data: [],
    loading: false,
    error: null
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsersThunk.pending, (state) => {
        state.loading = true;
        state.error = null;
      })
      .addCase(fetchUsersThunk.fulfilled, (state, action) => {
        state.data = action.payload;
        state.loading = false;
      })
      .addCase(fetchUsersThunk.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message || 'Failed to fetch users';
      });
  }
});

export default userSlice.reducer;

UI库选择

主流UI库对比

UI库 优点 缺点 适用场景
Ant Design 组件丰富,企业级,生态成熟 体积较大,定制化需要额外工作 后台管理系统,企业应用
Material UI 设计精美,响应式,支持深度定制 学习曲线略陡,体积较大 需要精美UI的应用,偏向Google设计风格
Chakra UI 轻量级,易于定制,响应式 组件相对较少 中小型项目,需要高度定制化
Tailwind UI 高度可定制,不限制组件结构 需要自己实现交互逻辑 需要独特设计的项目
Arco Design 轻量级,性能优秀,全局配置便捷 社区相对较小 注重性能的后台系统

推荐选择

对于后台管理系统,推荐Ant DesignArco Design:

bash 复制代码
# 安装Ant Design
pnpm add antd @ant-design/icons

# 或者安装Arco Design
pnpm add @arco-design/web-react @arco-design/icon

API请求封装

使用Axios封装

bash 复制代码
pnpm add axios
typescript 复制代码
// src/utils/request.ts
import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
import { message } from 'antd';
import { useAuthStore } from '@/stores/authStore';

const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 10000
});

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    const token = useAuthStore.getState().user?.token;
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    const { data } = response;
    // 根据实际后端接口结构调整
    if (data.code !== 0) {
      message.error(data.message || '请求错误');
      // 处理特定的错误码
      if (data.code === 401) {
        useAuthStore.getState().logout();
      }
      return Promise.reject(new Error(data.message || '请求错误'));
    }
    return data.data;
  },
  (error: AxiosError) => {
    if (error.response) {
      const status = error.response.status;
      let errorMsg = '未知错误';
      
      if (status === 401) {
        errorMsg = '未授权,请重新登录';
        useAuthStore.getState().logout();
      } else if (status === 403) {
        errorMsg = '拒绝访问';
      } else if (status === 404) {
        errorMsg = '请求的资源不存在';
      } else if (status === 500) {
        errorMsg = '服务器错误';
      }
      
      message.error(errorMsg);
    } else {
      message.error('网络错误,请检查您的网络连接');
    }
    return Promise.reject(error);
  }
);

// 封装GET请求
export function get<T>(url: string, params?: any, config?: AxiosRequestConfig): Promise<T> {
  return service.get(url, { params, ...config });
}

// 封装POST请求
export function post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
  return service.post(url, data, config);
}

// 封装其他请求方法
export function put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
  return service.put(url, data, config);
}

export function del<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
  return service.delete(url, config);
}

export default service;

性能优化策略

  1. 代码分割:使用React.lazy和Suspense进行组件懒加载
  2. 虚拟列表:对长列表使用react-window或react-virtualized
  3. 组件缓存:使用React.memo、useMemo、useCallback避免不必要的重渲染
  4. Web Vitals监控:使用web-vitals库监控核心性能指标
  5. 资源优化:使用现代图片格式(WebP),合理使用CDN
typescript 复制代码
// src/hooks/useVirtualScroll.ts
import { useState, useRef, useEffect } from 'react';

export function useVirtualScroll<T>(items: T[], itemHeight: number, containerHeight: number) {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);

  const visibleItemCount = Math.ceil(containerHeight / itemHeight) + 2;
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(startIndex + visibleItemCount, items.length);

  const visibleItems = items.slice(startIndex, endIndex);
  const totalHeight = items.length * itemHeight;
  const offsetY = startIndex * itemHeight;

  useEffect(() => {
    const handleScroll = () => {
      if (containerRef.current) {
        setScrollTop(containerRef.current.scrollTop);
      }
    };

    const container = containerRef.current;
    container?.addEventListener('scroll', handleScroll);
    return () => container?.removeEventListener('scroll', handleScroll);
  }, []);

  return {
    containerRef,
    visibleItems,
    totalHeight,
    offsetY,
  };
}

总结

搭建一个现代化的React后台管理系统,推荐以下选择:

  1. 基础技术栈:React 18/19 + TypeScript + pnpm + Vite
  2. 路由:React Router v6
  3. 状态管理
    • 中小型项目:Zustand
    • 大型复杂项目:Redux Toolkit
  4. UI库
    • 企业级后台系统:Ant DesignArco Design
    • 注重定制化:Chakra UI 或基于Tailwind CSS的组件库
  5. 工程化
    • ESLint + Prettier + Husky + Commitlint
    • 自动化测试:Jest + React Testing Library
  6. 构建工具:Vite(比CRA更快的开发体验)

以上最佳实践能够构建出一个现代化、可维护、高性能的React后台管理系统。根据项目具体需求,可以进行适当调整和优化。

哥哥姐姐弟弟妹妹们都看到这里不给个赞👍🏻

相关推荐
irving同学462381 小时前
Next.js 组件开发最佳实践文档(TypeScript 版)
前端
刺客-Andy1 小时前
React Vue 项开发中组件封装原则及注意事项
前端·vue.js·react.js
marzdata_lily2 小时前
从零到上线!7天搭建高并发体育比分网站全记录(附Java+Vue开源代码)
前端·后端
图扑软件2 小时前
智慧城市新基建!图扑智慧路灯,点亮未来城市生活!
大数据·javascript·人工智能·智慧城市·数字孪生·可视化·智慧路灯
小君2 小时前
让 Cursor 更加聪明
前端·人工智能·后端
顾林海2 小时前
Flutter Dart 异常处理全面解析
android·前端·flutter
很萌很帅的恶魔神ww2 小时前
HarmonyOS Next之组件之自定义弹窗(CustomDialog)
javascript
残轩2 小时前
JavaScript/TypeScript异步任务并发实用指南
前端·javascript·typescript
AR72 小时前
unplugin-vue-router 的基本使用
javascript
用户88442839014252 小时前
xterm + socket.io 实现 Web Terminal
前端