react+ts

490 阅读3分钟

创建一个 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;