React 如何封装一个可复用的 Ant Design 组件

news2025/5/25 10:56:02

文章目录

  • 前言
    • 一、为什么需要封装组件?
    • 二、 仿antd组件的Button按钮
    • 三、封装一个可复用的表格组件 (实战)
      • 1. 明确需求
      • 2. 设计组件 API
      • 3. 实现组件代码
      • 4. 使用组件
    • 三、封装组件的最佳实践
    • 四、进阶优化
  • 总结


前言

作为一名前端开发工程师,在日常项目中,我们经常会使用 UI 框架(如 Ant Design)来快速搭建界面。然而,直接使用框架组件可能会导致代码重复、样式不统一或功能扩展困难。本文将以封装一个 可复用的表格组件 为例,分享如何基于 Ant Design(antd)封装自己的业务组件,提升开发效率和代码质量。


一、为什么需要封装组件?

  1. 代码复用:避免重复编写相同的逻辑和样式。
  2. 统一风格:确保项目中的组件风格一致。
  3. 功能扩展:在基础组件上添加业务逻辑(如分页、搜索、权限控制等)。
  4. 降低维护成本:修改一处即可影响所有使用该组件的地方。

二、 仿antd组件的Button按钮

虽然有些人要问,antd组件库有现成的Button为啥不用,还要自己封装,难道你封装的比别人好?在此说明,本篇文章只是讲解封装思想,别人怎么封装的。

  1. 首先新建一个专门存放组件的文件夹—我的就叫做XLButton了
    index.tsx – 存放模板
    index.scss存放样式

在这里插入图片描述

  1. 确定模板和样式
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);
}
  1. 使用 -引入组件
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.Search
          placeholder="请输入搜索内容"
          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;

在这里插入图片描述
在这里插入图片描述

三、封装组件的最佳实践

  1. 类型安全

    • 使用 TypeScript 定义组件的 props 和内部状态。
    • 避免使用 any 类型,确保类型推断正确。
  2. 可扩展性

    • 通过 props 暴露必要的配置项(如分页、搜索、排序等)。
    • 支持自定义渲染(如 render 函数)。
  3. 默认值

    • 为可选 props 提供合理的默认值,减少重复代码。
  4. 样式隔离

    • 使用 CSS Modules 或 CSS-in-JS 避免样式污染。
    • 通过 classNamestyle 允许外部覆盖样式。
  5. 文档和示例

    • 编写清晰的 README,说明组件的用途、API 和使用示例。
    • 提供 Storybook 或类似工具展示组件的交互效果。

四、进阶优化

  1. 国际化(i18n)

    • 支持多语言文本(如空数据提示)。
  2. 主题定制

    • 通过 CSS 变量或主题配置文件支持主题切换。
  3. 性能优化

    • 使用 React.memo 避免不必要的重渲染。
    • 对大数据量表格使用虚拟滚动(如 react-window)。
  4. 单元测试

    • 编写 Jest 或 React Testing Library 测试用例,确保组件行为符合预期。

总结

通过封装 Ant Design 组件,我们可以:

  1. 提升开发效率,减少重复代码。
  2. 统一项目风格,降低维护成本。
  3. 快速响应业务需求变化,扩展组件功能。

封装组件的核心思想是 抽象公共逻辑,暴露灵活配置。希望本文的分享能帮助你在实际项目中更好地复用和扩展 Ant Design 组件!如果你有其他封装组件的经验或问题,欢迎在评论区交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2385299.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Canvas SVG BpmnJS编辑器中Canvas与SVG职能详解

Canvas详解与常见API 一、Canvas基础 核心特性 • 像素级绘图&#xff1a;Canvas是基于位图的绘图技术&#xff0c;通过JavaScript操作像素实现图形渲染&#xff0c;适合动态、高性能场景&#xff08;如游戏、数据可视化&#xff09;。 • 即时模式&#xff1a;每次绘制需手动…

dify多实例部署,一台机器部署多个dify实例

dify多实例部署 目的 实现在一台机器上&#xff0c;部署多个dify的实例。比如一个部署1.2版本&#xff0c;一个部署1.3版本。废话没有&#xff0c;直接上干货。 前提 你的电脑已经部署了一个dify实例&#xff0c;并成功运行。比如已经部署成功0.15.3版本。 步骤如下&#…

ML 48.机器学习之临床生存树(rpartSurv)

简介机器学习中生存树&#xff08;Survival Tree&#xff09;的原理详解 生存树是结合决策树与生存分析的机器学习模型&#xff0c;主要用于处理带有时间-事件数据&#xff08;包含删失数据&#xff09;的预测问题。其核心目标是&#xff1a;通过树状结构对数据进行递归分割&am…

HarmonyOS 应用开发,如何引入 Golang 编译的第三方 SO 库

本指南基于笔者临时修复的 ohos_golang_go 项目fork&#xff0c;解决HO 应用导入 cgo编译产物时的 crash 问题。 1. 下载 ohos_golang_go git clone https://gitcode.com/deslord/ohos_golang_go.git&#x1f4cc; 该仓库为笔者临时修复版本&#xff0c;修复了 CGO 编译模式下…

一体化雷达波明渠流量计简介

一、技术定义与核心原理 一体化雷达波明渠流量计是基于微波技术的全自动流量监测设备&#xff0c;采用 24G K 波段平面雷达技术&#xff0c;通过非接触式测量方式实现对明渠、河道、排水管网等场景的水位、流速及流量监测。其核心原理是利用雷达发射高频电磁波&#xff0c;经水…

Pr -- 耳机没有Pr输出的声音

问题 很久没更新视频号了&#xff0c;想用pr剪辑一下&#xff0c;结果使用Pr打开后发现耳机没有Pr输出的声音 解决方法 在编辑--首选项-音频硬件中设置音频硬件的输出为当前耳机设备

白皮精读:2024年国家数据基础设施建设指引【附全文阅读】

《国家数据基础设施建设指引》提出建设覆盖数据采集至安全全链条的新型基础设施,目标到 2029 年形成横向联通、纵向贯通的格局,聚焦数据可信流通、算力协同、高速传输、安全保障四大功能,明确技术架构与重点方向,强调政府与市场协同,分阶段推进试点及规模化部署,为数字中…

穿屏技巧:Mac-Windows一套鼠标键盘控制多台设备 (sharemouse6.0-Keygen)| KM-401A

文章目录 引言I sharemouse6.0介绍功能介绍关闭自动更新安装包II 安装系统对应的sharemouse软件Windowsmac版本III 知识扩展:SCP、FTP、SSH文件传输SCP配置SSH密钥免密登录FTP(File Transfer Protocal,文件传输协议)引言 基于USB进行同步键盘和鼠标事件,更流畅。 基于局域…

【写在创作纪念日】基于SpringBoot和PostGIS的各省东西南北四至极点区县可视化

目录 前言 一、空间检索简介 1、空间表结构 2、四至空间检索 二、前后端实现 1、后端实现 2、前端集成 三、成果展示 1、东部省份 2、西部省份 3、南部省份 4、北部省份 5、中部省份 四、总结 前言 在当今数字化时代&#xff0c;地理信息数据的分析与可视化对于众…

如何制作可以本地联网搜索的MCP,并让本地Qwen3大模型调用搜索回答用户问题?

环境: SearXNG Qwen3-32B-FP8 vllm 0.8.5 问题描述: 如何制作可以本地联网搜索的MCP,并让本地Qwen3大模型调用搜索回答用户问题? 解决方案: 一、安装searxng 1.按需新建模型相关文件夹 mkdir MCP chmod 777 /mnt/program/MCP2.配置conda源 nano ~/.condarc nano…

服务器硬盘虚拟卷的处理

目前的情况是需要删除逻辑卷&#xff0c;然后再重新来弄一遍。 数据已经备份好了&#xff0c;所以不用担心数据会丢失。 查看服务器的具体情况 使用 vgdisplay 操作查看服务器的卷组情况&#xff1a; --- Volume group ---VG Name vg01System IDFormat …

一个国债交易策略思路

该国债交易策略的核心在于通过分析历史价格数据来识别市场趋势&#xff0c;并在趋势确认时进行开仓操作。策略的设计思路结合了价格波动范围的计算和市场波动性的评估&#xff0c;旨在捕捉市场的短期趋势并控制风险。 首先&#xff0c;策略通过对过去5根K线的最高价和最低价进行…

【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之如何形成高斯椭球

【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之如何形成高斯椭球 文章目录 【三维重建】【3DGS系列】【深度学习】3DGS的理论基础知识之如何形成高斯椭球前言高斯函数一维高斯多维高斯 椭球基本定义一般二次形式 3D高斯椭球3D高斯与椭球的关系各向同性(Isotropic)和…

手写ES6 Promise() 相关函数

手写 Promise() 相关函数&#xff1a; Promise()、then()、catch()、finally() // 定义三种状态常量 const PENDING pending const FULFILLED fulfilled const REJECTED rejectedclass MyPromise {/*定义状态和结果两个私有属性:1.使用 # 语法&#xff08;ES2022 官方私有字…

【NLP 76、Faiss 向量数据库】

压抑与痛苦&#xff0c;那些辗转反侧的夜&#xff0c;终会让我们更加强大 —— 25.5.20 Faiss&#xff08;Facebook AI Similarity Search&#xff09;是由 Facebook AI 团队开发的一个开源库&#xff0c;用于高效相似性搜索的库&#xff0c;特别适用于大规模向…

软件名称:系统日志监听工具 v1.0

软件功能&#xff1a;一款基于 PyQt5 开发的 Windows 系统日志监听工具&#xff0c;适用于系统运维、网络管理、故障排查等场景&#xff0c;具备以下核心功能&#xff1a; 支持监听系统三大日志源&#xff1a;应用程序 / 系统 / 安全日志实时抓取新日志事件&#xff0c;自动滚…

Spring AI 之结构化输出转换器

截至 2024 年 2 月 5 日,旧的 OutputParser、BeanOutputParser、ListOutputParser 和 MapOutputParser 类已被弃用,取而代之的是新的 StructuredOutputConverter、BeanOutputConverter、ListOutputConverter 和 MapOutputConverter 实现类。后者可直接替换前者,并提供相同的…

Java虚拟机面试题:内存管理(上)

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

进程间通信I·匿名管道

目录 进程间通信&#xff08;IPC&#xff09; 含义 目的 分类 匿名管道 原理 创建过程 特性 四大情况 close问题 代码练习 简单通信 进程池 小知识 进程间通信&#xff08;IPC&#xff09; 含义 就是让不同的进程能看到同一份资源&#xff0c;实现数据交流。 …

Ubuntu Linux系统的基本命令详情

1.Ubuntu Linux是以桌面应用为主的Linux发行版操作系统 2.Ubuntu的用户使用 在登录系统一般使用在安装系统时建立的普通用户登录&#xff0c;如果要使用超级用户权限 #sudo ---执行命令 sudo passwd ---修改用户密码 su - root ---切换超级用户 系统的不同&#xff0c;命令也不…