游乐游手机版
首页/前端开发/文章详情

一次组件重构的开发哲学:从模糊到清晰

时间:2026-06-15 06:57
基于设备类型管理中“关联因子”弹窗的组件重构实践,探讨通用勾选与业务编辑的分层策略。通过插槽白名单透传、响应式实测验证,在模糊需求中先实现再迭代,最终实现职责清晰的薄封装。

从模糊到清晰:一次组件重构里的开发哲学

这个系列里,之前写过一篇关于「查表法」的文章——AI能写出功能,但优雅不优雅,考验的还是你自己的判断力。

从模糊到清晰:一次组件重构里的开发哲学

这次换个方向,不讲展示层的配色,而是聊聊通用组件与定制组件如何分层。故事原型来自设备类型管理里的「关联因子」弹窗——要在原有的勾选能力基础上,增加单位、上下限的行级编辑。

需求听起来不复杂,但刚开始的时候,画面其实相当模糊。


一、开发哲学:模糊是正常的,先上路

实现一个功能,最开始往往想不透。各种方案在脑子里排列组合,像某种「量子纠缠态」——每一种选择都会牵出新的不确定性:改通用组件?复制一份?抽composable?插槽怎么传?响应式会不会断?

想得太多,哪还有精力干活。

这次实践下来,可以慢慢摸出一条适合自己的节奏:

具体到这个需求,路径是这样展开的:

阶段当时的状态做出的决定
起点要在 DeviceTypeBindPollutant 里加 unit / 上下限编辑很模糊,方案很多
第一次收敛意识到这是「通用勾选」与「因子配置」的分叉不能大改 BaseCheckTable,保持职责单一
第二次收敛需要 tab 筛选、搜索、行级编辑、恢复默认参考 BaseCheckTable 重写一个专用组件
第三次收敛发现 70% 壳层重复,但列渲染需要定制薄包装 BaseCheckTable,编辑状态留在子组件
踏出那一步之后不确定性从「排列组合」变成「几个具体问题」插槽怎么透传?columns 里写 h() 响应式行不行?

很有意思的一点是:在真正动手之前,前路是模糊的;一旦选了方向、写出第一版,剩下的往往就只剩两三个技术点要确认。

不是一开始就想通了,是走起来才想通。


二、架构分层:通用勾选 vs 业务编辑

为什么不直接改 BaseCheckTable

BaseCheckTable 被十几个绑定弹窗共用——站房绑监测点、设备绑因子、类型绑类型……它们只需要:

  • 全部 / 未选 / 已选 tab
  • 搜索过滤
  • checkbox 勾选

如果在上面加 editColumns、行级编辑态、恢复默认按钮,通用组件会被设备类型因子的业务逻辑污染。10+ 个简单场景,为一个复杂场景买单,这笔账不划算。

为什么不一直独立到底?

第一版 DeviceTypeFactorCheckTableBaseCheckTable 的壳层几乎完整复制了一遍:wrapper、head、search、tableData / searchData / count*、vxe-grid 配置、scoped 样式。

能跑,但不优雅——FactorCheckTable.vue 形成了第三份重复。

最终的分工是:

DeviceTypeBindPollutant/          ← 弹窗编排、接口、校验
├── server.ts                     ← 数据合并(主数据 + 已保存配置)
├── DeviceTypeFactorCheckTable    ← 列定义 + editState + 行级编辑
└── (复用)BaseCheckTable        ← 勾选壳层:筛选 / 搜索 / grid
组件职责
BaseCheckTable勾选基础设施:filterType、searchName、searchData、vxe-grid 透传
DeviceTypeFactorCheckTable业务扩展:columns、editState、unit/上下限/操作列
DeviceTypeBindPollutant弹窗生命周期:open、提交、上下限校验

勾选是通用状态,编辑是业务状态——边界清楚了,代码自然就薄了。

最终的 DeviceTypeFactorCheckTable 模板只剩一层:


  
  
  
  

这才是真正想要的「优雅的状态」。


三、插槽透传与筛选:白名单,而不是无脑全传

要让上面的写法成立,中间层 BaseCheckTable 必须把父组件的 #unit 等插槽透传给内层 vxe-grid

最直接的想法是:


但这里有个坑:以后如果 BaseCheckTable 自己也要插槽呢?比如 #head-extra(头部额外按钮)、#search-extra(搜索框旁)、#footer(表格底部)——这些会被误传给 vxe-grid,轻则无效,重则奇怪报错。

怎么解决?COMPONENT_SLOT_NAMES 白名单

/** BaseCheckTable 自身插槽,其余插槽均透传给 vxe-grid */
const COMPONENT_SLOT_NAMES = ['head-extra', 'search-extra', 'footer'] as const;const gridSlotNames = computed(() =>
  Object.keys(slots).filter(
    (name) => !COMPONENT_SLOT_NAMES.includes(name as (typeof COMPONENT_SLOT_NAMES)[number])
  )
);

模板里:

数据流:

DeviceTypeFactorCheckTable  #unit="{ row, rowIndex }"
        ↓
BaseCheckTable              name="unit" v-bind="slotProps" />
        ↓
vxe-grid                    #unit 渲染单元格

规则很简单:组件自身插槽 → 显式写在模板对应位置;不在白名单里的 → 自动透传给 vxe-grid

目前 head-extra / search-extra / footer 是预留的——现在没人用,但结构已经就位。以后加组件插槽,只需两步:名字加入白名单 + 模板里挂

columns 与插槽的配合

vxe-grid 支持两种列渲染方式:

写法columns 配置渲染来源
具名插槽slots: { default: 'unit' }模板 #unit
内联函数slots: { default({ row }) { return h(...) } }columns 里的函数

这次用了第一种——columns 只声明插槽名,具体 UI 写在 template。可读性更好,按钮和表单组件一目了然。

const columns = [
  { title: '单位', field: 'unit', slots: { default: 'unit' } },
  { title: '下限', field: 'lowerLimit', slots: { default: 'lowerLimit' } },
  { title: '上限', field: 'upperLimit', slots: { default: 'upperLimit' } },
  { title: '操作', field: 'action', slots: { default: 'action' } },
];

如果全部收进 columnsh() 里,确实可以不再依赖插槽透传——但 action 列的按钮组会变得冗长。这是可读性与组合灵活性的取舍,没有唯一正确答案。


四、响应式实验:AI 不确定的事,实测说了算

在尝试把列渲染收进 columnsslots.default + h() 时,有一个理论上的担忧:

AI 的建议是:把 columns 做成 computed,显式依赖 editState.editIndex,更稳妥。

做了个最小 MVP 实验——只把 upperLimit 一列改成 h(InputNumber, ...) 写法,其余列保持 template 插槽,然后手动测:

  1. 点「修改」→ 输入框出现
  2. 输入数值 → 临时状态更新
  3. 点「确定」→ 写回 dataSource,回到展示态
  4. 筛选 / 搜索后编辑 → rowIndex 对应正确

结论:在当前这套 vxe-grid + Vue 3 + ant-design-vue 组合下,非响应式的 columns 常量 + 内联 slot 函数,编辑态切换是正常的。

原因大概是:vxe 渲染单元格时会反复调用 default({ row, rowIndex })editState.editIndex 变化 → 组件重渲染 → slot 函数重跑 → UI 切换。输入过程中则是 onUpdate:value 驱动子组件自身更新。

结论
computed(columns)可选优化,不是必须
内联 h()可行,有利于不依赖插槽透传
template 插槽可读性更好,响应式更直观
AI 的理论边界要验证,不能盲信

这件事挺有启发:AI 能列出理论风险,但你的实测才是项目里的真相。 没有观察到「点了修改但不切换」的现象,就不必提前加复杂度。

如果以后真遇到偶发不刷新,再考虑 computed(columns)reloadColumn——YAGNI,看见了再改。


五、内聚:子目录也是一种优雅

除了组件分层,还把模块收进了独立目录:

src/components/bindRelation/DeviceTypeBindPollutant/
├── DeviceTypeBindPollutant.vue
├── DeviceTypeFactorCheckTable.vue
├── server.ts          ← 从 bindRelation/server.ts 迁出并增强
└── type.ts

getDeviceTypeFactorRows 负责合并「因子主数据」与「已保存配置」,带上 defaultUnit / defaultLowerLimit / defaultUpperLimit 供恢复按钮使用。弹窗只关心 open → 展示 → modalOk 提交。

相关的放在一起,不相关的不要硬凑。 通用的 getPollutantsChecked 仍留在 bindRelation/server.tsDeviceFormModal 用——没有为了「内聚」而过度迁移。


六、回头看:优雅是走出来的

这次重构如果一上来就追求「完美架构」,可能会在这些问题上卡住:

  • 要不要改 BaseCheckTable
  • 插槽怎么透传?要不要白名单?
  • columnsh() 还是 template?要不要 computed
  • 要不要抽 useCheckTableFilter

每一个都值得想,但不值得在起点就想完

实际路径是:

  1. 先建子目录 + 专用表格,快速交付功能
  2. 发现壳层重复,尝试 h() 写法,验证响应式
  3. 确认可以薄包装 BaseCheckTable,补上插槽透传和白名单
  4. 列渲染回到 template 插槽——因为可读性更好

没有哪一步是浪费的。前面的「弯路」换来了后面的确定感。


写在最后

那篇文章里说过:AI 的代码质量,是你认知水平的投影。

这一篇补一句:你的认知,也是在一次次「先上路、再修正」里长出来的。

模糊不可怕。可怕的是因为模糊而站着不动,或者因为想一次想全而什么都写不出来。

车到山前必有路——但路是自己一步一步走出来的。


本文基于真实项目经验整理,手工起草文章大纲,AI 辅助润色,于 2026-06-11

来源:https://juejin.cn/post/7649932959655788594
上一篇Volta智能坑人:pnpm为何无视项目Node版本 下一篇TinyRobot AI对话组件库全组件使用教程
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
checked表单属性与CSS变量实现换肤原理
前端开发 · 2026-07-02

checked表单属性与CSS变量实现换肤原理

先聊一个有意思的现象:不需要编写任何 JavaScript,仅靠一个 :checked 伪类,就能驱动整个主题切换系统。听起来很神奇,但原理其实并不复杂——核心在于,:checked 是浏览器原生状态的实时镜像,而不是 JS 模拟出来的开关。 用户点击 ,或者用键盘空格键选中它,状态更新的那一刻,C

HTML meta标签页面定时跳转实现
前端开发 · 2026-07-02

HTML meta标签页面定时跳转实现

说到前端开发中最简洁的页面跳转方式,meta http-equiv= "refresh " 绝对算得上一个经典方案。不过别看它结构简单,格式上稍有疏忽,页面就可能原地卡死,或者直接跳到一个错误地址。下面把几个最容易踩坑的细节彻底讲清楚,帮你避开这些常见陷阱。 使用 http-equiv= "refresh

Cypress跨测试用例状态传递的不推荐但可选方案
前端开发 · 2026-07-02

Cypress跨测试用例状态传递的不推荐但可选方案

Cypress 默认的设计哲学很干脆:每个测试用例都必须是独立小王国,谁也不靠谁。这意味着 it() 执行前,浏览器上下文会被“一键还原”——页面状态、LocalStorage、Cookies 统统清空,强制维护测试隔离。这一规则让很多新手头疼:明明前一个测试已经创建了员工,后一个测试怎么就没法直接

全面深度解析HTML主体main标签唯一性原则与使用规范
前端开发 · 2026-07-02

全面深度解析HTML主体main标签唯一性原则与使用规范

在进行前端无障碍审计时,不少开发者会遇到一个奇怪的场景:浏览器不报错,但Lighthouse却直接标红“duplicate-main”。这其实是语义层与渲染层之间的根本差异。 为什么浏览器不报错但 Lighthouse 直接标红 duplicate-main 关键原因就在于:`main` 是语义锚点

HTML main标签在文档结构中的唯一性详解
前端开发 · 2026-07-02

HTML main标签在文档结构中的唯一性详解

先做一个快速检测:打开你最近开发的一个页面,按下 Ctrl+F 搜索 。如果搜索结果里出现2个以上,那这篇文章建议你认真读完。 本期要聊的主题,是HTML标签中一个看似简单、实际极易踩坑的核心知识点:main标签的唯一性。很多开发者知道这个标签的存在,但真正写到项目里,尤其是用了React、Vue这