切换日光/暗黑模式
086. 表格主体模块拆分与图片列扩展
学习目标
这一节开始拆分通用表格主体文件,并演示自定义图片列扩展。
学完后,你应该能理解:
- 为什么
createAutoTable主体文件需要拆分; - 表格能力为什么也要设计成模块;
- 模块安装顺序为什么重要;
- 如何通过类型扩展给表格对象追加能力;
- 如何新增一个自定义图片列;
- 列扩展和表格模块扩展有什么不同。
为什么要拆主体文件
通用表格主体函数已经有几百行。
里面混在一起的内容包括:
- 配置初始化;
- 状态管理;
- 表格方法;
- 事件处理;
- 生命周期逻辑;
- 渲染逻辑。
功能继续增加后,这个文件会越来越难读。
所以要把它拆成多个模块。
拆分不是为了好看。
它是后面做扩展机制的前提。
表格能力也要可扩展
前面已经实现了列可扩展。
不同项目可以新增自己的列类型。
但通用表格不只需要扩展列。
还需要扩展功能模块。
例如:
- 查询表单;
- 工具栏按钮;
- 表格顶部内容;
- 表格底部内容;
- 批量操作;
- 自定义状态;
- 自定义行为。
所以表格主体也要拆成模块。
先拆分,再讲扩展
这一节先做拆分。
模块扩展机制会在后面的内容里继续展开。
现在要先把主体函数里的几类职责拆出去。
可以先按注释区域拆:
- config;
- state;
- handler;
- lifecycle;
- render。
每一块都可以理解成一个模块。
配置模块
配置模块负责处理表格配置。
它会维护:
- 默认配置;
- 用户传入配置;
- 运行时配置;
- 更新配置的方法。
配置模块安装到表格对象后,表格对象就能拿到配置相关能力。
例如:
ts
autoTable.config
autoTable.runningConfig
autoTable.setConfig这里的具体命名以代码为准,关键是职责边界。
配置相关逻辑不再散落在主体函数里。
表格对象类型
原来可以通过函数返回值推导表格对象类型。
但这种方式不适合模块扩展。
因为后续模块会不断往表格对象上追加属性和方法。
所以需要定义一个可扩展的接口。
可以理解成:
ts
interface IAutoTable {}配置模块、状态模块、渲染模块都可以继续扩展这个接口。
这样模块安装后,TypeScript 也知道表格对象新增了什么能力。
状态模块
状态模块负责集中管理表格状态。
包括:
- 数据;
- 加载状态;
- 分页;
- 编辑行 ID 映射;
- 新增行 ID 映射;
- Form 实例管理;
- 更新这些状态的方法。
课程里把状态分成两类结果:
state:状态值;methods:修改状态的方法。
模块安装后,它们会被挂到表格对象上。
这样其他模块可以通过表格对象访问状态和方法。
Dispatch 类型
React 的 set 函数类型不只是“传一个值”。
它也可以接收一个函数。
例如:
ts
setData(nextData)
setData(prev => nextData)所以手写类型会比较麻烦。
可以使用 React 提供的 Dispatch 和 SetStateAction 类型。
对 JS 开发经验来说,先理解用途即可:它表示一个 React 状态更新函数。
模块安装
每个模块都是一个函数。
它接收表格对象和必要参数。
执行后,把自己的能力挂到表格对象上。
例如:
ts
installConfigModule(autoTable)
installStateModule(autoTable)
installRenderModule(autoTable)安装后,后续模块才能读取前面模块提供的内容。
这就引出一个重点:模块安装有顺序。
模块顺序
如果渲染模块要读取状态模块的数据,那么状态模块必须先安装。
否则渲染模块执行时,表格对象上还没有对应属性。
这和后端依赖注入、插件注册有点像。
你不能在服务还没有注册之前就使用它。
在通用表格里,模块顺序会影响:
- 类型是否可用;
- 状态是否存在;
- 方法是否能调用;
- 渲染逻辑是否能拿到完整上下文。
渲染模块
渲染模块负责返回真正的表格内容。
主体函数拆分后,主体文件可以变得很短。
它主要负责:
- 创建表格对象;
- 安装各个模块;
- 返回渲染函数或渲染结果。
渲染细节放到独立文件。
这样主体函数就不再承载所有 UI 逻辑。
临时保留 handler
事件处理模块在这一节还没有完全消除。
部分 handler 暂时保留。
原因是后续会用事件钩子的方式,让模块之间通信。
现在先完成主体拆分。
后面再把事件处理改造成更完整的模块机制。
这也是拆复杂代码时常见的节奏:
先拆出大块边界,再逐步优化模块之间的通信方式。
拆分后的主体函数
拆完后,主体文件从几百行变成几十行。
它不再直接维护所有状态和渲染细节。
它更像一个组装器。
组装器负责把配置模块、状态模块、渲染模块等装到同一个表格对象上。
这为后面“添加、删除、覆盖模块”留下空间。
表格模块能扩展什么
表格模块的目标是让调用方可以往表格不同位置加能力。
例如:
- 在表格顶部加查询表单;
- 在查询区右侧加按钮;
- 在表格工具栏加按钮;
- 在表格底部加统计信息;
- 添加自定义状态;
- 添加自定义事件。
这类能力不能只靠列扩展实现。
列扩展解决的是字段怎么显示、怎么编辑。
模块扩展解决的是表格整体功能怎么组合。
自定义图片列
这一节还演示了新增图片列。
图片列属于列扩展。
它需要做几件事:
- 定义图片列类型;
- 通过声明合并扩展列注册表;
- 定义图片列默认值;
- 注册图片列默认值填充逻辑;
- 在页面配置里使用
type: "image"。
图片列的字段
图片列可以有自己的额外参数。
例如:
ts
imageWidth?: number
imageHeight: number其中 imageHeight 可以设计成必填。
这样使用图片列时,如果不传高度,TypeScript 会提示。
这就是类型扩展的价值:新增业务列后,也能获得约束和提示。
图片列默认值
图片列可以给宽度默认值。
例如默认宽度为 70。
这样页面只需要传必要参数。
常用默认行为由列模块自己补齐。
图片渲染时,可以使用字段值作为图片路径。
如果项目里有静态资源访问前缀,也要在渲染时拼上。
执行注册代码
只定义图片列文件还不够。
注册代码必须被执行。
否则类型扩展和默认值填充逻辑不会真正进入运行流程。
课程里是在页面或入口里引入这个文件,让它执行注册。
等组件包发布后,这里的路径会变成包名。
现在本地还没发包,所以使用相对路径。
使用图片列
页面配置里可以这样使用图片列:
ts
{
title: "预览图",
dataIndex: "picUrl",
type: "image",
imageHeight: 50,
}表格会根据 type: "image" 找到图片列默认值和渲染逻辑。
如果宽度没有传,就使用图片列模块里的默认宽度。
图片编辑暂不实现
这一节只实现图片展示。
图片编辑涉及文件上传。
这部分需要更多代码。
所以图片列先不处理编辑态。
这也符合扩展组件的实现节奏:
先把类型、默认值、展示跑通,再补编辑和上传。
这一节的核心
通用表格开始从一个大函数变成模块化系统。
列扩展和模块扩展的边界是:
- 列扩展:新增字段类型,比如图片列、金额列、状态列;
- 模块扩展:新增表格能力,比如查询表单、工具栏、批量操作。
主体文件拆分后,后面才能更自然地支持模块添加、模块删除和模块覆盖。