组件设计原则

原则

一句话总结: ui 组件设计看 antd/element-ui,业务组件抽逻辑,dump 组件(展示型)纯模版,smart 组件带点 state。

组件分类: 一个模块下可能现有容器组件(layout/container),包含多个业务组件;一个业务组件可能包含多个 UI 组件和展示型组件;

组件的能力

●资源高内聚(组件资源内部高内聚,组件资源由自身加载控制)

●作用域独立(内部结构密封,不与全局或其他组件产生影响)

●自定义标签(定义组件的使用方式)

●可相互组合(组件间组装整合)

●接口规范化(组件接口有统一规范,或者是生命周期的管理)

组件的内容

●每个组件对应一个目录,组件所需的各种资源都在这个目录下就近维护;(最具软件工程价值)

●页面上的每个独立的可视/可交互区域视为一个组件;

●由于组件具有独立性,可以自由组合;

●页面是组件的容器,负责组合组件形成功能完整的界面;

●当不需要某个组件,或者想要替换组件时,可以整个目录删除/替换

组件的设计原则

●标准性: 任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件

●独立性: 描述了组件的细粒度,遵循单一职责原则,保持组件的纯粹性; 属性配置等 API 对外开放,组件内部状态对外封闭,尽可能的少与业务耦合

●复用与易用: UI 差异,消化在组件内部(注意并不是写一堆 if/else); 输入输出友好,易用

●追求短小精悍

●适用 SPOT 法则: Single Point Of Truth,就是尽量不要重复代码,出自《The Art of Unix Programming》

●避免暴露组件内部实现

●避免直接操作 DOM,避免使用 ref: 使用父组件的 state 控制子组件的状态而不是直接通过 ref 操作子组件

●入口处检查参数的有效性,出口处检查返回的正确性

●无环依赖原则(ADP): 如果组件之间存在循环依赖,会变成了“先有鸡还是先有蛋”的问题

●稳定抽象原则(SAP):

○组件的抽象程度与其稳定程度成正比,一个稳定的组件应该是抽象的(逻辑无关的),一个不稳定的组件应该是具体的(逻辑相关的)

○为降低组件之间的耦合度,我们要针对抽象组件编程,而不是针对业务实现编程

●避免冗余状态

○如果一个数据可由另一个 state 变换得到,那么这个数据就不是一个 state,只需写一个变换的处理函数,在 Vue 中可以使用计算属性

○如果一个数据是固定的,不会变化的常量,那么这个数据就如同 HTML 固定的站点标题一样,写死或作为全局配置属性等,不属于 state

○如果兄弟组件拥有相同的 state,那么这个 state 应该放到更高的层级,使用 props 传递到两个组件中

●合理的依赖关系: 父组件不依赖子组件,删除某个子组件不会造成功能异常

●扁平化参数: 除了数据,避免复杂的对象,尽量只接收原始类型的值

●良好的接口设计: 保证组件的属性和事件足够的给大多数的组件使用。

●API 尽量和已知概念保持一致

组件设计的边界

●页面层级不宜嵌套超过三层,切勿过度设计。

●这个组件可否(有必要)再分?太小会提升维护成本,太大又不够灵活和高复用性。性能是否会受影响。

●这个组件的依赖是否可再缩减?缩减组件依赖可以提高组件的可复用度

●这个组件是否对其它组件造成侵入?

●这个组件可否复用于其它类似场景中?需要考虑需要适用的不同场景,在组件接口设计时进行必要的兼容

●这个组件当别人用时,会怎么想?接口设计符合规范和大众习惯

●假如业务需要不需要这个功能,是否方便清除?

组件二次封装原则(以 vue 为例)

●主要以父组件传递数据给子组件来实现一些功能,子组件定义固定的展示样式,将具体要实现的业务逻辑抛出来给父组件处理

○不要在一个 vue 文件内,封装多个数据的入口处理,你应该遵守“单一职责”

○如果一个组件要耦合多个数据入口时,把这个组件改造为纯展示组件,跟业务解耦,把业务请求交给父组件。

●尽量保持 element-ui 组件原有的方法(可以使用 v-bind=”$attrs” 和 v-on=”$listeners”)

●$listeners 对象在 Vue 3 中已被移除。事件监听器现在是 $attrs 的一部分.(参看透传 Attributes)

○它们可以使得封装后的组件, “继承”原组件的几乎所有 v-bind 属性和 v-on 事件,且用法和作用与在原组件一样。

○如果确实要做更改组件原本的方法,也尽量让相似的方法方法名不变

○必须暴露组件的所有 props、事件、插槽和方法

■1)绑定$attrs 暴露 props。实现思路:用计算属性获取this.$attrs的值,然后用 v-bind 绑定

■2)绑定$listeners 暴露事件:v-on=”$listeners”。

●如果你手动写了v-on=”$listeners”,然后你又自己重写了其中的事件,那么这个事件就会执行两次。

●解决方法: 去掉重新的事件,使用 new$listeners 继承原有属性,并覆盖原有事件

■3)绑定 slots 和 scopedSlots 暴露插槽:插槽也是必须要暴露给父组件的,主要靠 Vue 提供的 slots 和 scopedSlots。

■4)暴露实例的方法:二次封装的组件暴露出实例,利用实例再调用 element 内部的方法

●搞清楚哪些是 props,不要修改 props 的数据

●载体分离原则: 内容和载体要分两个 vue 文件,内容是页面的主要内容,载体是展示的方式,例如弹窗、抽屉等等。