组件化
1. 组件化理解
就是将页面的某一部分独立出来,将这一部分的数据层(M)、视图层(V)和控制层(C)用黑盒的形式全部封装到一个组件内,暴露出一些开箱即用的函数和属性供外部调用。无论这个组件放到哪里去使用,它都具有一样的功能和样式,从而实现复用(只写一处,处处复用),这种整体化的思想就是组件化
每个组件都是独立的个体,都只负责一块功能。组件之间互相独立,通过特定的方式进行沟通。外部完全不用考虑组件的内部实现逻辑。一个好的前端组件,必须要把维护性,复用性,扩展性,性能做到极致
2. 组件化与模块化的区别
2.1 从历史发展角度来讲
随着前端开发越来越复杂、对效率要求越来越高,由项目级模块化开发,进一步提升到通用功能组件化开发,模块化是组件化的前提,组件化是模块化的演进
2.2 从整体概念来讲
- 模块化是一种分治的思想,述求是解耦,一般指的是 JavaScript 模块,比如用来格式化时间的模块
- 组件化是模块化思想的实现手段,述求是复用,包含了 template,style,script,script又可以由各种模块组成
2.3 从复用的角度来讲
- 模块一般是项目范围内按照项目业务内容来划分的,比如一个项目划分为子系统、模块、子模块,代码分开就是模块,位于架构业务框架层,横向分块
- 组件是按照一些小功能的通用性和可复用性抽象出来的,可以跨项目的,是可复用的模块,通常位于架构底层,被其他层所依赖
2.4 从划分的角度来讲
- 模块是从代码逻辑的角度进行划分,方便代码分层开发,保证每个功能模块的职能单一
- 组件是从 UI 界面的角度进行划分,前端的组件化,方便 UI 组件的重用
3. 为什么要前端组件化
随着前端项目复杂度的急剧增加,我们很容易遇到以下这些场景:
- 页面逻辑越来越多,代码越写越庞大,容易牵一发而动全身
- 同样的逻辑在多个地方重复编写,改一个问题要在多个地方进行同样的修改
以上场景带来的问题就是:
- 项目复杂度增加
- 重复性劳动多,效率低
- 代码质量差,不可控
因此前端组件化可以给我们带来:
- 增加代码的复用性,灵活性
- 提高开发效率,降低开发成本
- 便于各个开发者之间分工协作、同步开发
- 降低系统各个功能的耦合性,提高了功能内部的聚合性
- 降低代码的维护成本
4. 组件的划分
4.1 划分方法
尽可能抽象和解耦。不断抽象出一个跟业务没有关系的模块,它是可以继承的,这就是组件化设计的思维转换。
划分粒度:需要根据实际情况权衡,太小会提升维护成本,太大又不够灵活。
目前还没有一套原则和方法论来指导组件的划分,我们只能根据前人的经验再结合实际情况来进行组件的划分。
关于组件划分的一些建议:
- 组件之间的依赖应该尽可能的少。
- 单个组件代码量最好不要超过1000行。
- 组件划分的依据通常是业务逻辑、功能,要考虑各组件之间的关系是否明确,以及组件的可复用度。
- 每一个组件都应该有其独特的划分目的,有的是为了复用实现,有的是为了封装复杂度、清晰业务实现。
我经常的做法是:如果看到有多个页面都出现了这个重复元素,则抽取成一个组件。还有在开发之中发现结构相似的也可以考虑抽取成一个组件。没有必要在一开始就把所有都抽取成一个个组件。
4.2 组件分类
4.2.1 基础UI组件
这是最小化的组件,它们不依赖于其他组件。作为页面中最少的元素而存在,比如按钮、下拉菜单、对话框等。其中大部分是对原生 Web 元素的封装,例如: 、 、 ,它们以简单的形式存在。
- 在创建基础组件的过程中,要遵循一个基本原则:基础组件是独立存在的。它们可以共享配置,但是不能相互依赖,依赖意味着它不是基础组件。
- 像 antd、iview、element-ui 里提供的基本都是基础 UI 组件。
4.2.2 复合组件
复合组件是在多个基础的 UI 组件上的进一步结合。大部分复合组件,包含了一些复杂的组件,往往需要花很长的时间,才能变成一个可稳定使用的版本。复合组件包含以下几个部分:
- 表格:表格往往带有复杂的交互,比如固定行、固定列、可编辑、虚拟滚动等。由于其数据量大,往往又对性能有很高的要求。
- 图表:图表的门槛相对比较高,并且种类繁多,对于显示、交互的要求也高。
- 富文本编辑器:几乎是最复杂的组件,其功能需求往往与 Word 进行对比,其代码量可能接近 Word 的数量级。
4.2.3 业务组件
业务组件是我们在实现业务功能的过程中抽象出来的组件,其作用是在应用中复用业务逻辑。当它们涉及一些更复杂的业务情形时,就要考虑是否将这些组件放入组件库中。
- 通常是根据最小业务状态抽象而出,有些业务组件也具有一定的复用性,但大多数是一次性组件。
- 特点:UI可配置,业务逻辑完整。有完整的后台流程,数据结构。
5. 组件的设计原则
标准性:任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件。单一职责原则:一个组件只专注做一件事,且把这件事做好。一个功能如果可以拆分成多个功能点,那就可以将每个功能点封装成一个组件,当然也不是组件的颗粒度越小越好,只要将一个组件内的功能逻辑控制在一个可控的范围内即可。开闭原则:对扩展开放,对修改关闭。属性配置等 API 对外开放,组件内部状态对外封闭。追求短小精悍避免太多参数扁平化参数:除了数据,避免复杂的对象,尽量只接收原始类型的值。合理的依赖关系:父组件不依赖子组件,删除某个子组件不会造成功能异常适用SPOT(Single Point of Truth)法则:尽量不要重复代码追求无副作用复用与易用避免暴露组件内部实现入口处检查参数的有效性,出口处检查返回的正确性稳定抽象原则(SAP)
- 组件的抽象程度与其稳定程度成正比
- 一个稳定的组件应该是抽象的(逻辑无关的)
- 一个不稳定的组件应该是具体的(逻辑相关的)
- 为降低组件之间的耦合度,我们要针对抽象组件编程,而不是针对业务实现编程
良好的接口设计,API 尽量和已知概念保持一致
模块化
1. 模块化理解
1.1 模块化简介
模块化,就是把一个个文件看成一个模块,它们之间作用域相互隔离,互不干扰。一个模块就是一个功能,它们可以被多次复用。另外,模块化的设计也体现了分治的思想。什么是分治?维基百科 (opens new window)的定义如下:
字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
- 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
- 模块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信
1.2 模块化进程
全局function模式:将不同的功能封装成不同的全局函数
- 编码: 将不同的功能封装成不同的全局函数
- 问题: 污染全局命名空间, 容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系
function m1(){
//...
}
function m2(){
//...
}
namespace模式:简单对象封装
- 作用: 减少了全局变量,解决命名冲突
- 问题: 数据不安全(外部可以直接修改模块内部的数据)
let myModule = {
data: 'www.baidu.com',
foo() {
console.log(`foo() ${
this.data}`)
},
bar() {
console.log(`bar() ${
this.data}`)
}
}
myModule.data = 'other data' //能直接修改模块内部的数据
myModule.foo() // foo() other data
IIFE模式:匿名函数自调用(闭包)
- 作用: 数据是私有的, 外部只能通过暴露的方法操作
- 编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口
- 问题: 如果当前这个模块依赖另一个模块怎么办?
// index.html文件
<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
myModule.foo()
myModule.bar()
console.log(myModule.data) //undefined 不能访问模块内部数据
myModule.data = 'xxxx' //不是修改的模块内部的data
myModule.foo() //没有改变
</script>
// module.js文件
(function(window) {
let data = 'www.baidu.com'
//操作数据的函数
function foo() {
//用于暴露有函数
console.log(`foo() ${
data}`)
}
function bar() {
//用于暴露有函数
console.log(`bar() ${
data}`)
otherFun() //内部调用
}
function otherFun() {
//内部私有的函数
console.log('otherFun()')
}

最低0.47元/天 解锁文章
2万+

被折叠的 条评论
为什么被折叠?



