千家信息网

怎么编写插件机制优化基于Antd Table封装表格的混乱代码

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,本篇内容介绍了"怎么编写插件机制优化基于Antd Table封装表格的混乱代码"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家
千家信息网最后更新 2026年11月03日怎么编写插件机制优化基于Antd Table封装表格的混乱代码

本篇内容介绍了"怎么编写插件机制优化基于Antd Table封装表格的混乱代码"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

前言

最近在一个业务需求中,我通过在 Antd Table 提供的回调函数等机制中编写代码,实现了这些功能:

  • 每个层级缩进指示线

  • 远程懒加载子节点

  • 每个层级支持分页

最后实现的效果大概是这样的:

最终效果

这篇文章我想聊聊我在这个需求中,对代码解耦,为组件编写插件机制的一些思考。

重构思路

随着编写功能的增多,逻辑被耦合在 Antd Table 的各个回调函数之中,

  • 指引线的逻辑分散在 rewriteColumns, components中。

  • 分页的逻辑被分散在 rewriteColumns 和 rewriteTree 中。

  • 加载更多的逻辑被分散在 rewriteTree 和 onExpand 中

至此,组件的代码行数也已经来到了 300 行,大概看一下代码的结构,已经是比较混乱了:

export const TreeTable = rawProps => {   function rewriteTree() {     // ?加载更多逻辑     // ? 分页逻辑   }    function rewriteColumns() {     // ? 分页逻辑     // ? 缩进线逻辑   }    const components = {     // ? 缩进线逻辑   };    const onExpand = async (expanded, record) => {     // ? 加载更多逻辑   };    return ; };

这时候缺点就暴露出来了,当我想要改动或者删减其中一个功能的时候变得异常痛苦,经常在各个函数之间跳转查找。

有没有一种机制,可以让代码按照功能点聚合,而不是散落在各个函数中?

// ? 分页逻辑 const usePaginationPlugin = () => {}; // ? 加载更多逻辑 const useLazyloadPlugin = () => {}; // ? 缩进线逻辑 const useIndentLinePlugin = () => {};  export const TreeTable = rawProps => {   usePaginationPlugin();    useLazyloadPlugin();    useIndentLinePlugin();    return 
; };

没错,就是很像 VueCompositionAPI 和 React Hook 在逻辑解耦方面所做的改进,但是在这个回调函数的写法形态下,好像不太容易做到?

这时候,我回想到社区中一些开源框架提供的插件机制,好像就可以在不深入源码的情况下注入各个回调时机的用户逻辑。

比如 Vite 的插件[1]、Webpack 的插件[2] 甚至大家很熟悉的 Vue.use()[3],它们本质上就是对外暴露出一些内部的时机和属性,让用户去写一些代码来介入框架运行的各个时机之中。

那么,我们是否可以考虑把「处理每个节点、column、每次 onExpand」 的时机暴露出去,这样让用户也可以介入这些流程,去改写一些属性,调用一些内部方法,以此实现上面的几个功能呢?

我们设计插件机制,想要实现这两个目标:

  1. 鸿蒙官方战略合作共建--HarmonyOS技术社区

  2. 逻辑解耦,把每个小功能的代码整合到插件文件中去,不和组件耦合起来,增加可维护性。

  3. 用户共建,内部使用的话方便同事共建,开源后方便社区共建,当然这要求你编写的插件机制足够完善,文档足够友好。

不过插件也会带来一些缺点,设计一套完善的插件机制也是非常复杂的,像 Webpack、Rollup、Redux 的插件机制都有设计的非常精良的地方可以参考学习。

接下来,我会试着实现的一个最简化版的插件系统。

源码

首先,设计一下插件的接口:

export interface TreeTablePlugin {   (props: ResolvedProps, context: TreeTablePluginContext): {     /**      * 可以访问到每一个 column 并修改      */     onColumn?(column: ColumnProps): void;     /**      * 可以访问到每一个节点数据      * 在初始化或者新增子节点以后都会执行      */     onRecord?(record): void;     /**      * 节点展开的回调函数      */     onExpand?(expanded, record): void;     /**      * 自定义 Table 组件      */     components?: TableProps['components'];   }; }  export interface TreeTablePluginContext {   forceUpdate: React.DispatchWithoutAction;   replaceChildList(record, childList): void;   expandedRowKeys: TableProps['expandedRowKeys'];   setExpandedRowKeys: (v: string[] | number[] | undefined) => void; }

我把插件设计成一个函数,这样每次执行都可以拿到最新的 props 和 context。

context 其实就是组件内一些依赖上下文的工具函数等等,比如 forceUpdate, replaceChildList 等函数都可以挂在上面。

接下来,由于插件可能有多个,而且内部可能会有一些解析流程,所以我设计一个运行插件的 hook 函数 usePluginContainer:

export const usePluginContainer = (   props: ResolvedProps,   context: TreeTablePluginContext ) => {   const { plugins: rawPlugins } = props;    const plugins = rawPlugins.map(usePlugin => usePlugin?.(props, context));    const container = {     onColumn(column: ColumnProps) {       for (const plugin of plugins) {         plugin?.onColumn?.(column);       }     },     onRecord(record, parentRecord, level) {       for (const plugin of plugins) {         plugin?.onRecord?.(record, parentRecord, level);       }     },     onExpand(expanded, record) {       for (const plugin of plugins) {         plugin?.onExpand?.(expanded, record);       }     },     /**      * 暂时只做 components 的 deepmerge      * 不处理自定义组件的冲突 后定义的 Cell 会覆盖前者      */     mergeComponents() {       let components: TableProps['components'] = {};       for (const plugin of plugins) {         components = deepmerge.all([           components,           plugin.components || {},           props.components || {},         ]);       }       return components;     },   };    return container; };

目前的流程很简单,只是把每个 plugin 函数调用一下,然后提供对外的包装接口。mergeComponent 使用deepmerge[4] 这个库来合并用户传入的 components 和 插件中的 components,暂时不做冲突处理。

接着就可以在组件中调用这个函数,生成 pluginContainer:

export const TreeTable =  React.forwardRef((props, ref) => {   const [_, forceUpdate] = useReducer((x) => x + 1, 0)    const [expandedRowKeys, setExpandedRowKeys] = useState([])    const pluginContext = {     forceUpdate,     replaceChildList,     expandedRowKeys,     setExpandedRowKeys   }    // 对外暴露工具方法给用户使用   useImperativeHandle(ref, () => ({     replaceChildList,     setNodeLoading,   }));    // 这里拿到了 pluginContainer   const pluginContainer = usePluginContainer(     {       ...props,       plugins: [usePaginationPlugin, useLazyloadPlugin, useIndentLinePlugin],     },     pluginContext   );  })

之后,在各个流程的相应位置,都通过 pluginContainer 执行相应的钩子函数即可:

export const TreeTable = React.forwardRef((props, ref) => {   // 省略上一部分代码……    // 这里拿到了 pluginContainer   const pluginContainer = usePluginContainer(     {       ...props,       plugins: [usePaginationPlugin, useLazyloadPlugin, useIndentLinePlugin],     },     pluginContext   );    // 递归遍历整个数据 调用钩子   const rewriteTree = ({     dataSource,     // 在动态追加子树节点的时候 需要手动传入 parent 引用     parentNode = null,   }) => {     pluginContainer.onRecord(parentNode);      traverseTree(dataSource, childrenColumnName, (node, parent, level) => {       // 这里执行插件的 onRecord 钩子       pluginContainer.onRecord(node, parent, level);     });   }    const rewrittenColumns = columns.map(rawColumn => {     //  这里把浅拷贝过后的 column 暴露出去     //  防止污染原始值     const column = Object.assign({}, rawColumn);     pluginContainer.onColumn(column);     return column;   });    const onExpand = async (expanded, record) => {     // 这里执行插件的 onExpand 钩子     pluginContainer.onExpand(expanded, record);   };    // 这里获取合并后的 components 传递给 Table   const components = pluginContainer.mergeComponents() });

之后,我们就可以把之前分页相关的逻辑直接抽象成 usePaginationPlugin:

export const usePaginationPlugin: TreeTablePlugin = (   props: ResolvedProps,   context: TreeTablePluginContext ) => {   const { forceUpdate, replaceChildList } = context;   const {     childrenPagination,     childrenColumnName,     rowKey,     indentLineDataIndex,   } = props;    const handlePagination = node => {     // 先加入渲染分页器占位节点   };    const rewritePaginationRender = column => {     // 改写 column 的 render     // 渲染分页器   };    return {     onRecord: handlePagination,     onColumn: rewritePaginationRender,   }; };

也许机智的你已经发现,这里的插件是以 use 开头的,这是自定义 hook的标志。

没错,它既是一个插件,同时也是一个 自定义 Hook。所以你可以使用 React Hook 的一切能力,同时也可以在插件中引入各种社区的第三方 Hook 来加强能力。

这是因为我们是在 usePluginContainer 中通过函数调用执行各个 usePlugin,完全符合 React Hook 的调用规则。

而懒加载节点相关的逻辑也可以抽象成 useLazyloadPlugin:

export const useLazyloadPlugin: TreeTablePlugin = (   props: ResolvedProps,   context: TreeTablePluginContext ) => {   const { childrenColumnName, rowKey, hasNextKey, onLoadMore } = props;   const { replaceChildList, expandedRowKeys, setExpandedRowKeys } = context;    // 处理懒加载占位节点逻辑   const handleNextLevelLoader = node => {};    const onExpand = async (expanded, record) => {     if (expanded && record[hasNextKey] && onLoadMore) {       // 处理懒加载逻辑     }   };    return {     onRecord: handleNextLevelLoader,     onExpand: onExpand,   }; };

而缩进线相关的逻辑则抽取成 useIndentLinePlugin:

export const useIndentLinePlugin: TreeTablePlugin = (   props: ResolvedProps,   context: TreeTablePluginContext ) => {   const { expandedRowKeys } = context;   const onColumn = column => {     column.onCell = record => {       return {         record,         ...column,       };     };   };    const components = {     body: {       cell: cellProps => (                ),     },   };    return {     components,     onColumn,   }; };

至此,主函数被精简到 150 行左右,新功能相关的函数全部被移到插件目录中去了,无论是想要新增或者删减、开关功能都变的非常容易。

此时的目录结构:

目录结构

"怎么编写插件机制优化基于Antd Table封装表格的混乱代码"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

插件 逻辑 函数 代码 机制 节点 功能 组件 更多 用户 设计 处理 时机 流程 社区 钩子 混乱 接下来 就是 目录 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技公司 百单网互联网科技有限公司怎么样 河南云网互联网络科技有限公司 互联网科技工作委员会 互联网科技有限公司经营范围 奇酷互联网络科技公司 无线互联网科技 广州紫鲸互联网科技 华中科技有哪些互联网大佬 茂名市互联网科技产业园 福建科技产业互联网发展 米聚互联网科技南京有限公司排面 南博望互联网科技有限公司 科技赋能视角下互联网保险发展 广州百诚互联网科技有限公司官网 抖集互联网科技技术咨询 云鲸互联网络科技有限公司 江苏互联网智能科技产品出厂价 科技互联网专业工作会议发言稿 山东亿川互联网科技有限责任公司 互联网科技公司华为 涨乐互联网科技 工业互联网科技新基建 港股科技和互联网 广州市恰铎互联网科技有限公司 卓翼科技产业互联网合作 扫呗互联网科技有限公司 科技信息时代互联网发展 美国五大互联网科技公司市值多少 亚光科技低轨卫星互联网
0