创建一个 React + TypeScript 项目,并配置路由和页面的完整步骤如下。我们将使用 Create React App 来快速启动项目,并使用 react-router-dom 进行路由配置。
node版本:v20.0.0
1. 创建项目
首先,确保你安装了 Node.js 和 npm。然后使用 Create React App 创建一个 TypeScript 项目:
npx create-react-app my-react-ts-app --template typescript
2. 安装 react-router-dom
安装 react-router-dom 来进行路由管理:
npm install react-router-dom || yarn add react-router-dom
3. 设置基本的项目结构
在项目中创建一些基本的文件和目录结构:
my-react-ts-app/
│
├── src/
│ ├── components/
│ │ └── Layout.tsx
│ ├── pages/
│ │ ├── Home.tsx
│ │ └── About.tsx
│ ├── App.tsx
│ └── index.tsx
│
└── package.json
4. 配置路由和页面
src/pages/Home.tsx
import React from 'react';
const Home: React.FC = () => {
return <h1>Home Page</h1>;
};
export default Home;
src/pages/About.tsx
import React from 'react';
const About: React.FC = () => {
return <h1>About Page</h1>;
};
export default About;
src/components/Layout.tsx
import React, { ReactNode } from 'react';
import { Link } from 'react-router-dom';
// 定义 props 类型,包含 children
interface LayoutProps {
children: ReactNode;
}
const Layout: React.FC<LayoutProps> = ({ children }) => {
return (
<div>
<nav>
<Link to="/">Home</Link> | <Link to="/about">About</Link>
</nav>
<div>{children}</div>
</div>
);
};
export default Layout;
src/App.tsx
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Layout from './components/Layout';
import Home from './pages/Home';
import About from './pages/About';
const App: React.FC = () => {
return (
<Router>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Layout>
</Router>
);
};
export default App;
src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
src/index.css
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
5. 引入antd 配置左侧菜单和路由
项目结构
更新项目结构以支持左侧菜单:
my-react-ts-app/
│
├── src/
│ ├── components/
│ │ ├── Layout.tsx
│ │ └── Menu.tsx
│ ├── pages/
│ │ ├── Home.tsx
│ │ └── About.tsx
│ ├── App.tsx
│ └── index.tsx
│
└── package.json
安装 Ant Design 以及相关的样式文件:
npm install antd
npm install @ant-design/icons
||
yarn add antd
yarn add @ant-design/icons
配置 Ant Design
src/components/Menu.tsx
import React from 'react';
import { Menu } from 'antd';
import { Link } from 'react-router-dom';
const MainMenu: React.FC = () => {
return (
<Menu mode="vertical" theme="dark">
<Menu.Item key="home">
<Link to="/">Home</Link>
</Menu.Item>
<Menu.Item key="about">
<Link to="/about">About</Link>
</Menu.Item>
</Menu>
);
};
export default MainMenu;
src/App.tsx
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Layout from './components/Layout';
import Home from './pages/Home';
import About from './pages/About';
const App: React.FC = () => {
return (
<Router>
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Layout>
</Router>
);
};
export default App;
src/Layout.tsx
// import React, { ReactNode } from 'react';
// import { Link } from 'react-router-dom';
// // 定义 props 类型,包含 children
// interface LayoutProps {
// children: ReactNode;
// }
// const Layout: React.FC<LayoutProps> = ({ children }) => {
// return (
// <div>
// <nav>
// <Link to="/">Home</Link> | <Link to="/about">About</Link>
// </nav>
// <div>{children}</div>
// </div>
// );
// };
// export default Layout;
import React from 'react';
import { Layout } from 'antd';
import MainMenu from './Menu';
const { Header, Sider, Content } = Layout;
interface LayoutProps {
children: React.ReactNode;
}
const AppLayout: React.FC<LayoutProps> = ({ children }) => {
return (
<Layout style={{ minHeight: '100vh' }}>
{/* 将 Header 放在最外层的 Layout 中作为第一个子级 */}
<Header style={{ background: '#fff', padding: 0 }}>Header</Header>
<Layout>
{/* Sider 和 Content 在内层 Layout 中以水平排列 */}
<Sider>
<MainMenu />
</Sider>
<Content style={{ margin: '16px' }}>{children}</Content>
</Layout>
</Layout>
);
};
export default AppLayout;
配置 接口请求
目录结构
my-react-ts-app/
│
├── public/ # 公共文件目录
│ ├── index.html # HTML 模板
│ └── favicon.ico # 图标文件
│
├── src/ # 源代码目录
│ ├── components/ # 组件目录
│ │ ├── Layout.tsx # 布局组件
│ │ └── Menu.tsx # 菜单组件
│ │
│ ├── pages/ # 页面组件目录
│ │ ├── Home.tsx # Home 页面
│ │ └── About.tsx # About 页面
│ │
│ ├── services/ # 服务目录(例如 API 请求)
│ │ ├── axiosInstance.ts # Axios 实例配置
│ │ └── apiService.ts # API 请求封装
│ │
│ ├── styles/ # 样式文件目录
│ │ └── index.css # 样式文件
│ │
│ ├── App.tsx # 主应用组件
│ ├── index.tsx # 入口文件
│ └── react-app-env.d.ts # React 类型环境
│
├── package.json # 项目配置文件
├── tsconfig.json # TypeScript 配置文件
└── README.md # 项目文档
axiosInstance.ts
import axios, { InternalAxiosRequestConfig, AxiosHeaders } from 'axios';
// 创建 Axios 实例
const axiosInstance = axios.create({
baseURL: 'https://api.example.com', // 替换为你的 API 基础路径
timeout: 10000, // 请求超时时间
});
// 请求拦截器
axiosInstance.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
// 确保 headers 具有适当的类型
if (!config.headers) {
config.headers = new AxiosHeaders();
}
// 可以在请求头中添加 token
// config.headers.set('Authorization', `Bearer ${token}`);
return config;
},
(error) => Promise.reject(error),
);
// 响应拦截器
axiosInstance.interceptors.response.use(
(response) => response.data,
(error) => Promise.reject(error),
);
export default axiosInstance;
apiService.ts
import axiosInstance from './axiosInstance';
// GET 请求
export const getData = (endpoint: string, params?: Record<string, any>) => {
return axiosInstance.get(endpoint, { params });
};
// POST 请求 (URL 传参)
export const postDataWithUrlParams = (
endpoint: string,
params: Record<string, any>,
) => {
return axiosInstance.post(endpoint, null, { params });
};
// POST 请求 (Body 传参)
export const postDataWithBody = (
endpoint: string,
body: Record<string, any>,
) => {
return axiosInstance.post(endpoint, body);
};
文件使用
import React, { useEffect, useState } from 'react';
import {
getData,
postDataWithUrlParams,
postDataWithBody,
} from '../services/apiService';
const Home: React.FC = () => {
const [result, setResult] = useState<any>(null);
useEffect(() => {
// GET 请求示例
getData('/some-endpoint')
.then((data) => {
setResult(data);
console.log(data);
})
.catch((error) => console.error(error));
// POST 请求 (URL 传参) 示例
postDataWithUrlParams('/post-endpoint', { param1: 'value1' })
.then((data) => {
console.log(data);
})
.catch((error) => console.error(error));
// POST 请求 (Body 传参) 示例
postDataWithBody('/post-endpoint', { key: 'value' })
.then((data) => {
console.log(data);
})
.catch((error) => console.error(error));
}, []);
return <h1>Home Page</h1>;
};
export default Home;