学习目标
学完本章你应该能够:
- 说清
@路径别名为什么要"两处配置":Vite 打包侧(vite.config.ts的resolve.alias)和 TS 编辑器侧(tsconfig.app.json的paths)各管各的,缺一不可。 - 用
react-router-dom搭路由骨架:用createBrowserRouter+RouterProvider组织路由,并配好 404 / 403 兜底与通配重定向。 - 封装带拦截器的 Axios 实例:请求拦截器自动带 token,响应拦截器统一错误提示、登出跳转、返回
data.data。 - 封装 localStorage 与
.env:做到"一处定义、多处安全读取",避免原生 API 的坑。 - 串成可复用模板:把上面四块整合成一个 React + Vite + TS 后台管理初始化脚手架,新项目直接复用。
前置知识:
- 装过 Node / npm,跑过
npm create vite@latest(react-ts 模板)。 - 了解 React 函数组件、
useEffect、JSX 基本写法。 - 知道 HTTP 请求、token 登录的大致流程。
本章你会动手做的事:
- 按文中三步把
@别名配好,故意把paths写到tsconfig.json而非tsconfig.app.json,观察编辑器是否报红,再改回来。 - 用
createBrowserRouter写 3 条路由(/、/login、/*→ 404),在浏览器访问一个不存在的路径,确认被重定向到 404。 - 把
request.ts的响应拦截器改成"code 非 0 抛错 + 500001 清 token 跳登录",并在Login组件里真实发一次请求验证。
系统初始化设置
配置@路径
在 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"
},