文章目錄
- 前言
- 一、為什么需要封裝組件?
- 二、 仿antd組件的Button按鈕
- 三、封裝一個可復用的表格組件 (實戰)
- 1. 明確需求
- 2. 設計組件 API
- 3. 實現組件代碼
- 4. 使用組件
- 三、封裝組件的最佳實踐
- 四、進階優化
- 總結
前言
作為一名前端開發工程師,在日常項目中,我們經常會使用 UI 框架(如 Ant Design)來快速搭建界面。然而,直接使用框架組件可能會導致代碼重復、樣式不統一或功能擴展困難。本文將以封裝一個 可復用的表格組件 為例,分享如何基于 Ant Design(antd)封裝自己的業務組件,提升開發效率和代碼質量。
一、為什么需要封裝組件?
- 代碼復用:避免重復編寫相同的邏輯和樣式。
- 統一風格:確保項目中的組件風格一致。
- 功能擴展:在基礎組件上添加業務邏輯(如分頁、搜索、權限控制等)。
- 降低維護成本:修改一處即可影響所有使用該組件的地方。
二、 仿antd組件的Button按鈕
雖然有些人要問,antd組件庫有現成的Button為啥不用,還要自己封裝,難道你封裝的比別人好?在此說明,本篇文章只是講解封裝思想,別人怎么封裝的。
- 首先新建一個專門存放組件的文件夾—我的就叫做XLButton了
index.tsx – 存放模板
index.scss存放樣式
- 確定模板和樣式
import "./index.scss";interface XLButtonProps {type?: "primary" | "default" | "danger";size?: "small" | "middle" | "large";children?: React.ReactNode;
}const XLButton: React.FC<XLButtonProps> = (props) => {const typeStyle =props.type === "primary"? "xl-button-primary": props.type === "danger"? "xl-button-danger": "xl-button-default";const sizeStyle =props.size === "small"? "xl-button-small": props.size === "middle"? "xl-button-middle": "xl-button-large";return (<button className={`${typeStyle} ${sizeStyle}`}>{props.children || "Default Button"}</button>);
};export default XLButton;
// index.scss
button {color: #fff;width: 123px;height: 36px;border-radius: 6px;outline: none;border: none;margin-right: 20px;
}.xl-button-primary,
.xl-button-danger,
.xl-button-default {padding: 8px 16px;border: none;border-radius: 4px;cursor: pointer;transition: all 0.3s ease;
}// 尺寸樣式
.xl-button-small {width: 60px;height: 30px;padding: 0 8px;font-size: 12px;
}.xl-button-middle {height: 32px;padding: 4px 16px;font-size: 14px;
}.xl-button-large {height: 40px;padding: 8px 24px;font-size: 16px;
}.xl-button-primary {background-color: #1890ff;color: white;
}.xl-button-danger {background-color: #ff4d4f;color: white;
}.xl-button-default {background-color: #fff;border: 1px solid #d9d9d9;color: #333;
}.xl-button-default:hover {border-color: #1890ff !important;color: #1890ff;
}.xl-button-danger:hover,
.xl-button-primary:hover {opacity: 0.8;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
- 使用 -引入組件
import XLButton from "../Components/XLButton";
const Home = () => {return (<div><h1>仿antd按鈕</h1><XLButton type="danger">我是danger</XLButton ><XLButton type="primary">我是primary</XLButton ><XLButton >我是默認</XLButton ></div>);
};export default Home;
三、封裝一個可復用的表格組件 (實戰)
有了上面的思想,現在讓我們來封裝一個自己的表格組件。進一步強化
1. 明確需求
假設我們需要封裝一個支持以下功能的表格組件:
- 分頁(pagination)
- 搜索(search)
- 列排序(sortable columns)
- 自定義列渲染
- 加載狀態(loading)
- 空數據提示(empty text)
2. 設計組件 API
首先,設計組件的 props
接口,明確外部傳入的參數:
import React from 'react';import { Table, Input, Button, Space } from 'antd';import type { TableProps, ColumnsType } from 'antd';interface EnhancedTableProps<RecordType extends object = any> {columns: ColumnsType<RecordType>; // 表格列配置dataSource: RecordType[]; // 表格數據loading?: boolean; // 加載狀態emptyText?: React.ReactNode; // 空數據提示pagination?: TableProps<RecordType>['pagination']; // 分頁配置onSearch?: (value: string) => void; // 搜索回調onSort?: (field: string, order: 'ascend' | 'descend') => void; // 排序回調}
3. 實現組件代碼
以下是完整的組件實現代碼:
import React, { useState } from "react";
import { Table, Input, Space } from "antd";
import type { TableProps } from "antd";
import type { ColumnsType } from "antd/es/table";
import { SearchOutlined } from "@ant-design/icons";interface EnhancedTableProps<RecordType> {columns: ColumnsType<RecordType>;dataSource: RecordType[];loading?: boolean;emptyText?: React.ReactNode;pagination?: TableProps<RecordType>["pagination"];onSearch?: (value: string) => void;onSort?: (field: string, order: "ascend" | "descend") => void;
}const MyTable = <RecordType extends object>({columns,dataSource,loading = false,emptyText = "暫無數據",pagination,onSearch,onSort,
}: EnhancedTableProps<RecordType>) => {const [searchValue, setSearchValue] = useState("");// 處理搜索const handleSearch = () => {if (onSearch) {onSearch(searchValue.trim());}};// 處理排序const handleChange: TableProps<RecordType>["onChange"] = (pagination,filters,sorter) => {if (Array.isArray(sorter)) {// 處理多列排序的情況const firstSorter = sorter[0];if (firstSorter?.field && onSort) {onSort(firstSorter.field as string,firstSorter.order as "ascend" | "descend");}} else if (sorter?.field && onSort) {// 處理單列排序的情況onSort(sorter.field as string, sorter.order as "ascend" | "descend");}};return (<div>{/* 搜索框 */}<Space style={{ marginBottom: 16 }}><Input.Searchplaceholder="請輸入搜索內容"value={searchValue}onChange={(e) => setSearchValue(e.target.value)}onSearch={handleSearch}enterButton={<SearchOutlined />}/></Space>{/* 表格 */}<Table<RecordType>columns={columns}dataSource={dataSource}loading={loading}pagination={pagination}onChange={handleChange}locale={{emptyText,}}/></div>);
};export default MyTable;
4. 使用組件
在其他地方使用封裝好的 EnhancedTable
組件:
import XLBuuton from "../Components/XLButton";
import MyTable from "../Components/MyTable";
import type { ColumnsType } from "antd/es/table";interface User {key: number;name: string;age: number;address: string;
}const columns: ColumnsType<User> = [{title: "姓名",dataIndex: "name",key: "name",sorter: (a: User, b: User) => a.name.localeCompare(b.name),},{title: "年齡",dataIndex: "age",key: "age",sorter: (a: User, b: User) => a.age - b.age,},{title: "地址",dataIndex: "address",key: "address",},
];const dataSource: User[] = [{key: 1,name: "張三",age: 32,address: "北京市朝陽區",},{key: 2,name: "李四",age: 42,address: "上海市浦東新區",},{key: 3,name: "李四",age: 42,address: "上海市浦東新區",},{key: 4,name: "李四",age: 42,address: "上海市浦東新區",},{key: 5,name: "李四",age: 42,address: "上海市浦東新區",},{key: 6,name: "李四",age: 42,address: "上海市浦東新區",},{key: 7,name: "李四",age: 42,address: "上海市浦東新區",},
];const Home = () => {return (<div><h1>仿antd按鈕</h1><XLBuuton type="danger">我是danger</XLBuuton><XLBuuton type="primary">我是primary</XLBuuton><XLBuuton>我是默認</XLBuuton><hr /><h1>自定義表格--包含分頁和搜索排序</h1><MyTable<User>columns={columns}dataSource={dataSource}pagination={{ pageSize: 5 }}onSearch={(value) => console.log("搜索:", value)}onSort={(field, order) => console.log("排序:", field, order)}/></div>);
};export default Home;
三、封裝組件的最佳實踐
-
類型安全:
- 使用 TypeScript 定義組件的
props
和內部狀態。 - 避免使用
any
類型,確保類型推斷正確。
- 使用 TypeScript 定義組件的
-
可擴展性:
- 通過
props
暴露必要的配置項(如分頁、搜索、排序等)。 - 支持自定義渲染(如
render
函數)。
- 通過
-
默認值:
- 為可選
props
提供合理的默認值,減少重復代碼。
- 為可選
-
樣式隔離:
- 使用 CSS Modules 或 CSS-in-JS 避免樣式污染。
- 通過
className
或style
允許外部覆蓋樣式。
-
文檔和示例:
- 編寫清晰的 README,說明組件的用途、API 和使用示例。
- 提供 Storybook 或類似工具展示組件的交互效果。
四、進階優化
-
國際化(i18n) :
- 支持多語言文本(如空數據提示)。
-
主題定制:
- 通過 CSS 變量或主題配置文件支持主題切換。
-
性能優化:
- 使用
React.memo
避免不必要的重渲染。 - 對大數據量表格使用虛擬滾動(如
react-window
)。
- 使用
-
單元測試:
- 編寫 Jest 或 React Testing Library 測試用例,確保組件行為符合預期。
總結
通過封裝 Ant Design 組件,我們可以:
- 提升開發效率,減少重復代碼。
- 統一項目風格,降低維護成本。
- 快速響應業務需求變化,擴展組件功能。
封裝組件的核心思想是 抽象公共邏輯,暴露靈活配置。希望本文的分享能幫助你在實際項目中更好地復用和擴展 Ant Design 組件!如果你有其他封裝組件的經驗或問題,歡迎在評論區交流!