React Admin初始化环境

2021-08-02T22:13:02+08:00 | 4分钟阅读 | 更新于 2021-08-02T22:13:02+08:00

@

学习目标

学完本章你应该能够:

  1. 说清 @ 路径别名为什么要"两处配置":Vite 打包侧(vite.config.ts 的 resolve.alias)和 TS 编辑器侧(tsconfig.app.json 的 paths)各管各的,缺一不可。
  2. 用 react-router-dom 搭路由骨架:用 createBrowserRouter + RouterProvider 组织路由,并配好 404 / 403 兜底与通配重定向。
  3. 封装带拦截器的 Axios 实例:请求拦截器自动带 token,响应拦截器统一错误提示、登出跳转、返回 data.data。
  4. 封装 localStorage 与 .env:做到"一处定义、多处安全读取",避免原生 API 的坑。
  5. 串成可复用模板:把上面四块整合成一个 React + Vite + TS 后台管理初始化脚手架,新项目直接复用。

前置知识:

  • 装过 Node / npm,跑过 npm create vite@latest(react-ts 模板)。
  • 了解 React 函数组件、useEffect、JSX 基本写法。
  • 知道 HTTP 请求、token 登录的大致流程。

本章你会动手做的事:

  1. 按文中三步把 @ 别名配好,故意把 paths 写到 tsconfig.json 而非 tsconfig.app.json,观察编辑器是否报红,再改回来。
  2. 用 createBrowserRouter 写 3 条路由(/、/login、/* → 404),在浏览器访问一个不存在的路径,确认被重定向到 404。
  3. 把 request.ts 的响应拦截器改成"code 非 0 抛错 + 500001 清 token 跳登录",并在 Login 组件里真实发一次请求验证。
React Admin 初始化四块基础设施路径别名 @vite.config aliastsconfig paths路由骨架createBrowserRouter404 / 403 兜底Axios 封装拦截器带 token统一错误 / 登出存储与配置localStorage 封装.env 环境变量

系统初始化设置

配置@路径

在 React + Vite 项目中配置 @ 路径别名,需要同时配置两个地方,缺一不可: 第一步:安装 @types/node

npm install -D @types/node

第二步:配置 vite.config.ts 让 Vite 打包工具认识 @ 指向 src 目录:

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

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
})
复习提示:path.resolve(__dirname, ‘./src’) 会生成 src 目录的绝对路径,这是 Vite 要求的,不能写相对路径。

第三步:配置 TypeScript 路径映射

这一步是让 VS Code 等编辑器能识别路径、自动补全、点击跳转,否则编辑器会报红。 根据你项目的 tsconfig 结构,在 tsconfig.json 或 tsconfig.app.json 的 compilerOptions 中添加:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
复习提示:⚠️ 注意:用 Vite 创建的 React + TS 项目通常有三个 tsconfig 文件:tsconfig.json、tsconfig.app.json、tsconfig.node.json。你应该把 paths 配置写在 tsconfig.app.json 中(它负责 src 下源码的编译规则)。

配置完成后的效果

// 之前要这样写
import Home from '../../pages/home'

// 现在可以这样写
import Home from '@/pages/home'
import Home from '@/home'  // 如果 home 文件在 src 根目录下

配置路由

Api 路由配置文件

import { RouterProvider } from 'react-router-dom'
import Router from './router'
import './App.css'

function App() {
  return <RouterProvider router={router} />
}

export default App
import { createBrowserRouter, Navigate } from 'react-router-dom'

const router = [
  {
    path: '/',
    element: <div className='welcome'>Welcome</div>
  },
  {
    path: '/login',
    element: <div className='welcome'>Login</div>
  },
  {
    path: '/*',
    element: <Navigate to='/404' />
  },
  {
    path: '/404',
    element: <div className='welcome'>404</div>
  },
  {
    path: '/403',
    element: <div className='welcome'>403</div>
  }
]

export default createBrowserRouter(router)
import { Button, Result } from 'antd';
import { useNavigate } from 'react-router';

function NotFound() {
  const navigate = useNavigate();
  const handleClick = () => {
    navigate('/welcome')
  }

  return <Result status={404} title="404" subTitle="抱歉,您访问的页面不存在。" extra={
    <Button type='primary' onClick={handleClick}>回首页</Button>
  }/>
}

export default NotFound;

Axios

第一步:安装 Axios

npm install axios

第二步: Axios使用

import axios from 'axios'
import { useEffect } from 'react'

export default function Login() {
  useEffect(() => {
    axios
      .post('/users', {
        params: {
          ID: 12345
        }
      })
      .then(res => {
        console.log('res:', res)
      })
      .catch(error => {
        console.log('error:', error)
      })
  }, [])
  return <div>Login</div>
}

第三步:封装 Axios

utils/request.ts

import axios, { type InternalAxiosRequestConfig, type AxiosResponse } from "axios"
import { message } from "antd";


const instance = axios.create({
    baseURL: "http://127.0.0.1:8000",
    timeout: 3000,
    timeoutErrorMessage: "请求超时,请稍后重试...",
    withCredentials: true,
})


instance.interceptors.request.use(
    (config): InternalAxiosRequestConfig => {
        return config;
    },
    (err: unknown): Promise<never> => {
        return Promise.reject(err);
    }
)

instance.interceptors.response.use(
    (response: AxiosResponse): AxiosResponse | Promise<never> => {
        const data = response.data as Record<string, unknown>
        if (data.code === 4001) {
            window.location.href = "./login";
        } else if (data.code !== 200) {
            message.error(String(data.msg))
            return Promise.reject(data)
        }
        return response
    },
    (err: unknown): Promise<never> => {
        return Promise.reject(err);
    }
)

export default {
    get: (url: string, params?: Record<string, unknown>) => {
        return instance.get(url, { params })
    },
    post: (url: string, params?: Record<string, unknown>) => {
        return instance.post(url, params)
    },
    put: (url: string, params?: Record<string, unknown>) => {
        return instance.put(url, params)
    },
    delete: (url: string, params?: Record<string, unknown>) => {
        return instance.delete(url, { params })
    }
}

使用封装后的 Axios 实例

import request from './utils/request'
import { useEffect } from 'react'

export default function Login() {
  useEffect(() => {
    request
      .post('/users', {
        params: {
          ID: 12345
        }
      })
      .then(res => {
        console.log('res:', res)
      })
      .catch(error => {
        console.log('error:', error)
      })
  }, [])
  return <div>Login</div>
}

LocalStorage 封装

utils/storage.ts

export default {
    set: (key: string, value: any) => {
        localStorage.setItem(key, JSON.stringify(value));
    },
    remove: (key: string) => {
        localStorage.removeItem(key);
    },
    get: (key: string) => {
        return localStorage.getItem(key);
    },
    clear: () => {
        localStorage.clear();
    }
}

环境变量配置

1.在根目录下创建env文件,如.env.development 2.在env文件中配置环境变量,如VITE_API_URL=http://localhost:3000 3.在项目中使用环境变量,const apiBaseUrl = import.meta.env.VITE_API_URL 4.在package.json添加如下代码

"scripts": {
    "dev": "vite --mode development",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
About Me

没什么想介绍的,一个很大众的码农…

喜欢代码,车,马,真的是 🐎

讨厌别人让我给自己的代码写注释 最厌烦别人的程序没有写注释

目标

学AI,加油!加油!