在这篇博客中,我将向读者们介绍如何使用
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
组件定义了应用程序的不同路由,使其能够在点击菜单链接时正确切换到不同的页面。