在这篇博客中,我将向读者们介绍如何使用
React18 + vite + TypeScript + antDesign
搭建一个前端框架。React
是一种流行的JavaScript
库,用于构建用户界面。vite
是一个快速的前端构建工具,能够快速创建现代化的应用程序。TypeScript
是一种开源的编程语言,它可以帮助开发者在编写JavaScript
代码时减少错误。antDesign
是一个流行的 UI 库,提供了众多现成的组件可以让开发者更轻松地构建出漂亮的视图。在本博客结束时,读者将了解如何将这些技术组合在一起,创建出一个普普通通的前端框架。
本篇博客将分为以下几个步骤:
- 准备工作:安装必需的软件和库
- 创建一个新的项目
- 安装
React
和antDesign
库 - 配置
TypeScript
- 构建应用程序
- 创建自定义的
antDesign
组件 - 集成
Redux
- 使用路由和导航器
请注意,本篇文章的重点是搭建前端框架,因此我们不会涉及特定领域的业务逻辑。
准备工作:安装必需的软件和库
在开始之前,你需要在计算机上安装必要的工具和库。确保已经安装了 Node.js 和 npm。你可以到 Node.js 官方网站下载适合自己的安装文件,并进行安装。 安装完成后,你还需要全局安装 create-vite-app 和 TypeScript,可以直接在命令行工具中执行以下命令:
npm i -g create-vite-app typescript
创建一个新的项目
在安装完必要的软件后,我们需要用 create-vite-app
工具创建一个新项目。从命令行中运行以下命令:
create-vite-app my-app --template react-ts
这将会创建一个名为 my-app 的新项目,使用 react-ts 模板来初始化应用程序。这里我们使用 TypeScript 模板,可以帮助我们在编写代码时发现一些常见的错误。
命令执行完后,你应该可以看到一个文件夹 my-app 被创建出来了。进入 my-app 目录,执行以下命令开始项目:
cd my-app
npm i
npm run dev
这些命令将会跑起应用程序,并在浏览器中打开 localhost:3000
端口以供你访问。
安装 React 和 antDesign 库
我们的下一步是安装 React
和 antDesign
库。在命令行中输入以下命令:
npm i react react-dom antd @types/react @types/react-dom
这将会安装最新的 React
,antDesign
库以及类型声明文件。请确保使用的版本是最新的,以便获得最佳的性能和最新功能。
按需加载antd
antd
默认支持基于 ES modules
的 tree shaking
,直接引入 import { Button } from 'antd';
就会有按需加载的效果。
高级配置
目前我这边安装的最新antd版本是5.xx版本,如果项目中需要有主题配置等高级设置,可根据官方文档安装craco
配置 TypeScript
接下来,我们需要为 TypeScript
配置项目。创建 tsconfig.json
文件,并添加以下内容:
{
"compilerOptions": {
"target": "es6",
"module": "esnext",
"jsx": "react-jsx",
"sourceMap": true,
"strict": true,
"esModuleInterop": true
},
"include": ["src"]
}
这个配置文件告诉 TypeScript
将代码编译为 ES6
模块,并使用 React
的 JSX
语法。此外,我们还启用了严格模式,并支持 ES
模块的导入/导出功能。
构建应用程序
接下来,我们需要将 React
和 antDesign
库集成到实际的应用程序中。
在 App.tsx
文件中,我们需要导入 React
和 antDesign
组件:
import React from 'react';
import { Layout, Menu } from 'antd';
import './App.css';
const { Header, Content, Footer } = Layout;
function App() {
return (
<Layout className="layout">
<Header>
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
<Menu.Item key="3">nav 3</Menu.Item>
</Menu>
</Header>
<Content style={{ padding: '0 50px' }}>
<div className="site-layout-content">Content</div>
</Content>
<Footer style={{ textAlign: 'center' }}>XXX XXXXX ©2023 Created by XXX XXXX</Footer>
</Layout>
);
}
export default App;
在这个例子中,我们导入了 antDesign
的 Layout
和 Menu
组件,创建了一个基本的应用程序布局。
接下来,我们需要在 index.tsx
文件中将 React
组件渲染到页面上:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
这将会渲染 App 组件到页面上。现在,我们可以运行 npm run dev
命令以查看我们的更改。
npm run dev
创建自定义的 antDesign 组件
为了让我们的应用程序看起来更独特,并且符合具体的业务需求,我们可以创建自定义的 antDesign
组件。这里我们将会创建一个自定义的 Button
组件,这个组件将会是 antDesign
的 Button
组件的改进版。
在 src/components
目录中,创建一个新文件 CustomButton.tsx
,添加以下内容:
import React from 'react';
import { Button } from 'antd';
import { ButtonProps } from 'antd/lib/button';
interface CustomButtonProps extends ButtonProps {
size?: 'small' | 'large' | 'default';
shape?: 'round' | 'circle' | 'default';
}
const CustomButton: React.FC<CustomButtonProps> = ({
size = 'default',
shape = 'default',
...rest
}) => {
let className = '';
if (size !== 'default') {
className += ` ${size}`;
}
if (shape !== 'default') {
className += ` ${shape}`;
}
return <Button className={className.trim()} {...rest} />;
};
export default CustomButton;
这个自定义的 Button
组件继承自 antDesign
的 Button
组件,并且添加了两个新的 props
:size
和 shape
。这两个 props
用于定义按钮的大小和形状。
接下来,在 App.tsx
中使用 CustomButton
组件:
import React from 'react';
import { Layout, Menu } from 'antd';
import CustomButton from './components/CustomButton';
import './App.css';
const { Header, Content, Footer } = Layout;
function App() {
return (
<Layout className="layout">
<Header>
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
<Menu.Item key="1">nav 1</Menu.Item>
<Menu.Item key="2">nav 2</Menu.Item>
<Menu.Item key="3">nav 3</Menu.Item>
<CustomButton type="primary" size="large" shape="round">
Sign up
</CustomButton>
</Menu>
</Header>
<Content style={{ padding: '0 50px' }}>
<div className="site-layout-content">Content</div>
</Content>
<Footer style={{ textAlign: 'center' }}>XXX XXXX ©2023 Created by XXX XXXX</Footer>
</Layout>
);
}
export default App;
这里我们在应用程序头部菜单中添加了一个 Sign up
按钮,并且使用了我们自定义的 CustomButton
组件。
集成 Redux
现在,我们已经拥有了一个基本的应用程序,并且添加了自定义的 antDesign
组件。接下来,我们要考虑如何管理应用的状态。这里将介绍如何将 Redux
集成到我们的应用程序中。
首先,我们需要安装 Redux
库:
npm install redux react-redux @types/react-redux
接下来,在 src
目录下创建 redux
目录,并在其中创建 actions
、reducers
、store
、types
目录,以及 rootReducer.ts
文件。我们的 Redux
代码将被分布在这些目录中,并将 rootReducer.ts
文件作为统一入口。
在 types
目录下创建 types.ts
文件,该文件定义我们应用程序的所有状态:
import { ActionType } from 'typesafe-actions';
import * as actions from '../actions';
export type TodosAction = ActionType<typeof actions>;
export interface TodoItem {
id: number;
title: string;
completed: boolean;
}
export interface TodosState {
readonly todos: TodoItem[];
readonly error?: string;
readonly loading: boolean;
}
我们的应用程序状态将包含一个 todos
数组、一个 loading
标志符和可能的错误信息。接下来,我们需要创建 reducer
。在 reducers/todosReducer.ts
文件中,添加以下内容:
import { TodosAction, TodosState } from '../types/types';
import { createReducer } from 'typesafe-actions';
import { addTodo, removeTodo, toggleTodo, fetchTodos, fetchTodosSuccess, fetchTodosFailure } from '../actions';
const initialState: TodosState = {
todos: [],
loading: false,
};
export const todosReducer = createReducer<TodosState, TodosAction>(initialState)
.handleAction(addTodo, (state, action) => {
const id = state.todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1;
const title = action.payload;
return {
...state,
todos: [...state.todos, { id, title, completed: false }],
};
})
.handleAction(removeTodo, (state, action) => ({
...state,
todos: state.todos.filter((todo) => todo.id !== action.payload),
}))
.handleAction(toggleTodo, (state, action) => ({
...state,
todos: state.todos.map((todo) =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
),
}))
.handleAction(fetchTodos, (state) => ({
...state,
loading: true,
}))
.handleAction(fetchTodosSuccess, (state, action) => ({
...state,
loading: false,
todos: action.payload,
}))
.handleAction(fetchTodosFailure, (state, action) => ({
...state,
loading: false,
error: action.payload,
}));
这个 reducer
处理了基于数组的 todos
对象的所有操作,包括添加、删除、切换和远程获取 todos
。接下来,我们要将上面的 reducer
导入 rootReducer.ts
文件中:
import { combineReducers } from 'redux';
import { todosReducer } from './reducers/todosReducer';
const rootReducer = combineReducers({
todos: todosReducer,
});
export type RootState = ReturnType<typeof rootReducer>;
export default rootReducer;
这里,我们将 todosReducer
和其他 possible reducer
合并成一个 rootReducer
。
接下来,我们需要创建 actions
。在 actions/todosActions.ts
文件中,添加以下内容:
import { createAction } from 'typesafe-actions';
import { TodoItem } from '../types/types';
export const addTodo = createAction('ADD_TODO')<string>();
export const removeTodo = createAction('REMOVE_TODO')<number>();
export const toggleTodo = createAction('TOGGLE_TODO')<number>();
export const fetchTodos = createAction('FETCH_TODOS')();
export const fetchTodosSuccess = createAction('FETCH_TODOS_SUCCESS')<TodoItem[]>();
export const fetchTodosFailure = createAction('FETCH_TODOS_FAILURE')<string>();
这个文件导出了一些 actions
,通过 createActions
工具创建。我们可以使用这些 actions
来通知 reducer
执行相应的操作。
接下来,我们需要创建 store
。在 store/index.ts
文件中,添加以下内容:
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';
import rootReducer from '../rootReducer';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(sagaMiddleware))
);
export default store;
这个文件创建了 Redux store
,并将 sagaMiddleware
用作中间件。sagaMiddleware
是用于管理副作用的中间件,比如异步操作和处理 WebSocket
等。这里我们使用了 composeWithDevTools
工具来增强 store
,这样我们可以在浏览器中使用 Redux
开发工具调试。
到目前为止,我们已经在应用程序中添加了 React
、antDesign
、TypeScript
和 Redux
,这些都是构建框架的关键要素。
使用路由和导航器
接下来,我们要添加路由和导航器,以实现应用程序不同页面之间的切换。这里我们将使用 react-router
和 react-router-dom
库。
首先,我们需要安装这两个库:
npm install react-router react-router-dom @types/react-router-dom
接下来,在 App.tsx
文件上方添加以下 imports
:
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
这里,我们导入了 Router
、Switch
、Route
和 Link
组件。Router
组件是 react-router
的基础组件,用于定义应用程序的主要路由。Link
组件用于在应用程序中定义导航链接。
接下来,在 App.tsx
文件中,将其中 Layout
、Header
、Menu
和 CustomButton
包裹在 Router
组件内:
import React from 'react';
import { Layout, Menu } from 'antd';
import './App.css';
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
import CustomButton from './components/CustomButton';
const { Header, Content, Footer } = Layout;
function App() {
return (
<Router>
<Layout className="layout">
<Header>
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={['-1']}>
<Menu.Item key="-1">
<Link to="/">Home</Link>
</Menu.Item>
<Menu.Item key="1">
<Link to="/about">About Us</Link>
</Menu.Item>
<Menu.Item key="2">
<Link to="/contact">Contact Us</Link>
</Menu.Item>
<CustomButton type="primary" size="large" shape="round">
Sign up
</CustomButton>
</Menu>
</Header>
<Content style={{ padding: '0 50px' }}>
<div className="site-layout-content">
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/contact">
<Contact />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Content>
<Footer style={{ textAlign: 'center' }}>XXX XXXX ©2023 Created by XXX XXXX</Footer>
</Layout>
</Router>
);
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Contact() {
return <h2>Contact</h2>;
}
export default App;
这里,我们使用 Switch
和 Route
组件定义了应用程序的不同路由,使其能够在点击菜单链接时正确切换到不同的页面。