如何设计一个组件,更通用,更易维护呢?
UML 类图
UML 中有一个在 OOP 类设计中经常使用的类型,称为 UML 类图,前端对于通过图解辅助设计的方法值得参考,对于前端组件,该图标可以显示:
- State
- Props
- Methods
- 与其他组件关系(Relationship to other components)
松耦合
耦合
是指实体彼此依赖的术语。松散耦合的实体应该能够独立运行,而不依赖于其他模块。
就前端组件而言,耦合的主要部分是
- 组件的功能依赖于其付组件及其传递的
props
的多少 - 内部使用的子组件
- 引用的部分,如第三方模块或用户脚本
紧密耦合的组件往往不容易被复用,当父组件的一个子组件或一系列子组件只能在该父组件才能够正常发挥作用时,就说明组件设计的有问题了,这是一个明显的信号。
设计组件时,应该考虑到更加通用的使用场景,需要站在更高的角度去看待,那么很多组件将具有更好的适用性。
其实现在框架都是在为这个目标努力,比如单向数据流,组件之间交互原则:props down, event(callback) up。
辅助代码分离
将辅助代码分离出来放在特定的地方,比如
- 配置代码
- 假数据
- 大量非技术说明文档
因为在尝试处理组件的核心代码时,你不希望看到技术无关的一些说明,因为会带来心里负担甚至打断思路
逻辑下沉
这是我总结的一个概念,开发组件的一个好方法是使得他们包含渲染它们所需的最小的 JavaScript,对于数据处理、数据获取或事件处理逻辑,理想情况下应该将通用的部分移入外部 js 中。这里就让我思考到一个文件组织的问题。
这一部分的逻辑可能分为两个部分
- 业务逻辑
- 通用工具
在编码的过程中,应该尽可能思考,这个 action 能不能更加通用化,如果可以,就设计成一个 util 函数,这就是你的沉淀了。
数据范围
在有 redux 等优秀状态管理库存在的情况下,我们很容易将一些不需要的数据也放入 store 中进行管理,需要注意的是,该部分数据的生命周期是伴随整个 APP 的,如果非全局数据交由 store 进行管理,会导致内存浪费。
在很多的跨级数据传递的场景中,我们应该学会考虑使用 context api 进行管理。
组件分类
这个观点应该是来自于 dan 大神在 medium 的一篇博客,但文章首部,有一段 2019 年的追加,大致内容为
我的观点已经发生改变,甚至不在推荐使用这种方式拆分组件,我发现它有用的主要原因是,它使我能够将复杂的有状态逻辑与组件的其他方面分离开来。Hooks 可以做到同样的事情,同时作者希望我们不要对内容太认真,不要当成教条被强制执行
容器组件和展示组件,如何区分两者呢?
展示组件可以简单的理解为 props => view,也就是说 props 决定 view 的显示,具有如下特点
- 关注点:
how things look
- 使用
this.props.children
包含其他内容 - 不依赖下 app 的其他部分,比如 flux 的 action 和 store
- 不会定义数据如何读取,如何改变
- 很少有自己的状态变量,即使有,也是 UI 的状态变量
- 一般是函数级组件,除非它们需要状态,lifecycle hooks,或性能优化处理
而容器组件的特点为
- 关注点:
how things work
- 通常没有自己的 DOM 标签,除了一些 wrapping divs
- 可能依赖下 app 的其他部分,比如 flux 的 action 和 store
- 通常有自己的状态
这种方式的好处
- 关注点分离
- 更好的可复用性
- 修改 UI 不会影响到逻辑
- 迫使你抽离一些
layout components
,使用this.props.children
,而不是在不同的容器组件中声明一些相同的标签和布局
何时引入容器:从展示组件触发,当你发现你的组件层级树,很多中间组件并不需要某个 props,它只是做了中间人转发角色的时候,就可以考虑引入容器组件了。
基本原则
DRY 原则、SRP 原则、LKP 原则、纯函数优先