【自用】从组件化讲起的重构
上一期讲到了模块化,模块化聚焦于js/css等代码的逻辑组织,核心是将代码拆分为独立模块,明确模块间的依赖关系,避免全局变量污染和依赖混乱,还可以实现按需加载。
和模块化相关的还有组件化的概念。如果说模块化是 “代码的拆分组织”,那么组件化就是“UI和逻辑的封装复用”。但组件化并不是模块化的更细一步,因为它们的拆分维度和目标不同,是两个平行但协同的设计思想。
一、组件化
组件化是将页面拆分为独立的、可复用的 “组件单元”,每个组件包含完整的结构(HTML)、样式(CSS)、逻辑(JS),像搭积木一样组合成复杂页面。
下面说说组件化的原则。
1.1 原则
1. 单一职责原则、可组合原则:组件功能尽可能小,然后组合
单一职责:一个组件应该只负责一个明确的功能,不承担过多职责。
可组合:而不同组件应该可通过组合形成更复杂的组件或页面,像 “搭积木” 一样灵活。
实践:设计原子组件(如输入框),使组件职责尽可能单一。复合组件(如表单)。支持组件树结构。
2. 可复用、可配置原则:组件功能尽可能通用,然后配置
可复用:需考虑多场景,避免与特定业务强绑定。
可配置:通过外部参数或扩展机制,让组件适应不同场景,无需修改源码。
例子:提取共性逻辑(如按钮的 “加载状态”“禁用状态” 是所有按钮的共性)。避免硬编码(文本、样式、业务规则不应写死,通过参数动态传入)。 Props 定义配置项(如按钮的size、type、disabled)。slots支持自定义内容(如卡片组件的header插槽允许用户自定义头部内容)。
3. 高内聚低耦合、 封装性原则:组件对外只暴露必要接口
高内聚低耦合(结果):组件内紧密围绕核心功能,没有冗余内容。组件间依赖关系弱,通过明确的接口通信。
封装性(手段):隐藏内部实现细节,只暴露必要接口。
实践:props要精简,保持最小化,只暴露必要的配置选项。合理使用slot插槽代替高度定制化的props参数
4.一致性原则
实践:组件的设计(命名、接口、样式)遵循统一规范,降低团队协作成本。
1.2 组件通信模式
1.props + $emit(父子通信)
父组件通过 props 向子组件传递数据,子组件通过 $emit 触发自定义事件向父组件传递数据,是最基础的父子通信方式。
2.provide 与 inject(跨层级通信)
祖先组件通过 provide 提供数据,任意子孙组件通过 inject 注入数据,无需显式传递,适合深层级通信(主要用于组件库开发)。
3.EventBus(兄弟 / 跨组件通信)
通过创建一个全局事件总线(如 new Vue() 实例),组件间通过 $on 监听事件、$emit 触发事件实现通信,适合中小型项目。
4.parent / $root(跨层级通信)
子组件通过 $parent 访问父组件实例,或通过 $root 访问根组件实例,进而获取其他组件的数据(需注意组件层级关系,避免过度耦合)。
5.ref 和 $attrs(父子通信)
ref:父组件通过 ref 获取子组件实例,直接访问子组件的属性或方法(this.$refs.child.xxx)。$attrs:子组件未在 props 中声明的属性会集中在 $attrs 中,可通过 v-bind="$attrs" 向下传递(适用于多级传递)。
6.Vuex/Pinia(全局状态管理)
通过集中式存储管理应用的所有组件状态,解决复杂组件关系(如跨级、无关联组件)的数据共享问题,适用于中大型项目。
1.3 与组件化相关的痛点场景
UI代码/逻辑代码明显大量重复
组件的职责边界模糊
组件间的依赖关系混乱,如子组件直接修改父组件数据,或通过全局变量传递状态
初期设计的组件无法满足新需求
组件间的依赖关系随业务增长变得复杂
部分组件因过度设计变得臃肿
拓展:OOP的原则solid
SOLID原则:
S(单一职责原则):一个类 / 组件仅负责一项职责。
O(开放封闭原则):功能应该是原子化的,可拓展而不是需要修改。
L(里氏替换原则):子类需完全兼容父类的行为。
I(接口隔离原则):接口需拆分为细粒度专用接口。
D(依赖倒置原则):高层与低层模块均依赖抽象,抽象不依赖细节。
二、重构
重构的核心是在不改变代码原有功能的前提下,通过调整代码结构、逻辑或实现方式,提升其可维护性、可复用性与性能。
为什么博主要从组件化讲到重构呢,其实是因为现实中业务代码往往是 “逐步堆砌” 的,初期可能为了快速实现功能,代码逻辑混杂。那么不管是CR的时候或者是后期需要优化,重构是我们常常遇到的。而组件化又是重构中更加常见的动作,比如提取公共组件,维护优化公共组件等。所以我把这些方法和原则提取成组件化这个专题并且我想在组件化的后面继续讲一讲重构的相关事项。
2.1 分类
框架语言迁移,重新组织目录结构,提取重复代码,优化算法等。
2.2 要求
1.可读性
简化复杂逻辑(拆分超大函数,提取重复逻辑,避免嵌套ifelse)
统一代码风格(规范命名等)
写注释
2.可维护性
减少硬编码和魔法值
向后兼容
降低耦合度(减少组件 / 模块间的直接依赖)
提高内聚性(让单个模块专注于单一职责,避免 "万能类 / 函数")
3.可扩展性
设计弹性架构(如使用工厂模式等,支持新增功能时最小化修改)
预留扩展点(如配置化设计,通过参数修改功能)
兼容多场景(避免过度定制化,让代码适用于更广泛的业务需求)
4.性能优化
减少不必要的计算(如缓存中间结果、避免循环内的重计算)
优化资源利用(如合理释放内存、减少 IO 操作次数)
提升响应速度(如异步处理非关键流程、优化渲染 / 加载性能)
5.可靠性
减少潜在 bug(类型检查、异常捕获、边界条件完善等)
可追溯性(完善日志记录,便于问题定位和排查)
三、迁移
由于现在越来越多的技术栈的涌现,以及项目在迭代更新的过程中可能最开始选择的技术栈已经不再适用了,那么可能就会有迁移的工作。
首先要确保功能的完整性和一致性。在做迁移工作时,可以整体过一遍文件结构比如用伪代码简单记录,再结合ai辅助分析,避免遗漏,最后对于各类情景进行模拟。
第二,要注意迁移前和迁移后使用的语言的不同的特点。以及数据格式是否有不同。
第三,在迁移的过程中也要去主动地识别一些重复的逻辑,或者是不优雅的逻辑。在迁移的过程中就可以顺便把重构的事情给做了。
最后,迁移可能会涉及团队协同,需要及时同步进展,沟通疑问。重要的动作需要互相知会。
#优化##重构##组件化##前端#