12 前端工程化

组件化


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. 组件的设计原则

  1. 标准性:任何一个组件都应该遵守一套标准,可以使得不同区域的开发人员据此标准开发出一套标准统一的组件。
  2. 单一职责原则:一个组件只专注做一件事,且把这件事做好。一个功能如果可以拆分成多个功能点,那就可以将每个功能点封装成一个组件,当然也不是组件的颗粒度越小越好,只要将一个组件内的功能逻辑控制在一个可控的范围内即可。
  3. 开闭原则:对扩展开放,对修改关闭。属性配置等 API 对外开放,组件内部状态对外封闭。
  4. 追求短小精悍
  5. 避免太多参数扁平化参数:除了数据,避免复杂的对象,尽量只接收原始类型的值。
  6. 合理的依赖关系:父组件不依赖子组件,删除某个子组件不会造成功能异常
  7. 适用SPOT(Single Point of Truth)法则:尽量不要重复代码
  8. 追求无副作用
  9. 复用与易用
  10. 避免暴露组件内部实现
  11. 入口处检查参数的有效性,出口处检查返回的正确性
  12. 稳定抽象原则(SAP)
  • 组件的抽象程度与其稳定程度成正比
  • 一个稳定的组件应该是抽象的(逻辑无关的)
  • 一个不稳定的组件应该是具体的(逻辑相关的)
  • 为降低组件之间的耦合度,我们要针对抽象组件编程,而不是针对业务实现编程
  1. 良好的接口设计,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()')
  }
  
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

taciturn丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值