前端组件化

 

传统的开发方式

 

 

概述

 

根据页面的渲染过程来编写代码结构。像:init -> getData -> processData -> bindevent -> report -> xxx 。 方法之间线性跳转,你大概也能感受这样代码弊端。随着页面逻辑越来越复杂,这条” 过程线“ 也会越来越长,并且越来越绕。加之缺少规范约束,其他项目成员根据各自需要,在” 过程线“ 加插各自逻辑,最终这个页面的逻辑变得难以维护。

 

前端组件化_第1张图片

 

 

面向过程

 

 

最为实际的一种思考,也比较符合普通人的思维方式,它考虑的更多是实际的实现,从上而下的步步求精。不过缺点也比较明显,因为每一步都依赖它的上一步计算结果,导致整个功能为一个整体,牵一发而动全身,另外也限制了复用的可能性。

 

 

前端组件化_第2张图片

 

组件化编程

 

 

概述

 

组件就是将一段UI样式和其对应的功能作为独立的整体去看待,无论这个整体放在哪里去使用,它都具有一样的功能和样式,从而实现复用,这种整体部分的思想就是组件化。不难看出,组件化设计就是为了增加复用性,灵活性,提高系统设计,从而提高开发效率。与模块化类似,组件化是页面层次上,按照功能纬度来进行分割,从而达到隔离复杂度的目的。

 

康威定律:软件源代码的组织结构要与开发团队的组织结构尽量保持一致

 

前端组件化_第3张图片

 

 

目的

  • 降低页面复杂度
  • 提高可维护性
  • 增加可读性
  • 方便单元测试
  • 提高可复用性
  • 提高开发效率
  • 提高维护效率(分而治之)

 

 

 

 

结构化编程

 

功能性降解拆分,我们可以将一个大型问题拆分为多个高级函数,再将高级函数拆分为多个低级函数,如此无限递归下去,层层分解,且每一个函数也可以通过结构化编程来分解,最终可以拆解成更小、可证明的函数单元。

但是形式化证明并没有发生,因为程序和数学还是不同,程序没办法被证明,只能够被证伪。我们写的单元测试,或者由测试人员进行的测试只能证明在当前环境是足够正确的。

 

禁用goto无条件转移语句

 

 

 

组件划分

 

通用组件:

 

具有业务通用性,普适与一般的业务场景,为了复用而被创建,可被沉淀下来被多处使用,可以被单独维护和扩展。目前市面上开源的组件都是通用组件。

 

业务组件:

 

包含完整的功能逻辑,具备控制层职责,为高级策略组件,是稳定抽象层,整个功能中最稳定的部分,负责高级策略的定义,整体轮廓的搭建,及其他策略类的创建与配置。智能组件

 

原子组件:

 

业务组件中直接依赖的低级策略组件,作为业务组件的一部分存在,通过组合功能完成功能,只负责展示相关的职责。木偶组件

 

 

 

 

 

示意图

 

前端组件化_第4张图片

 

 

前端组件化_第5张图片

 

 

 

架构图

 

前端组件化_第6张图片

 

 

 

 

 

 

组件聚合原则

 

该原则告诉我们应该将哪些类放在一个组件中。

 

三大原则:

 

复用/发布等同原则(REP)

 

为复用性而组合

 

软件复用的最小颗粒度等于其发布的最小颗粒度,该原则从发布角度来指导我们构建组件,它告诉我们一个组件是由一组有关联的类或者模块组成,它们之间至少有一个共同的主题或者大方向。

 

 

共同闭包原则(CCP)

 

为维护性而组合

 

 

单一职责原则在组件层次上的应用(将由于相同原因被修改,且会同时修改的东西放在一起)

 

 

 

共同复用原则(CRP)

 

为避免不必要的发布而切分

 

 

接口隔离原则在组件层次上的应用,建议我们不要依赖不需要的类,保持组件的最小可用性,不要依赖不需要的东西。

 

 

三个原则彼此之间存在竞争关系,而软件架构就是在这三个原则进行取舍。

 

前端组件化_第7张图片

 

 

在项目早期,我们会牺牲一部分通用性,而偏向可维护性和最小可用性的指导原则来进行开发,在项目逐渐迭代的过程中,决策重心会逐渐往可复用性方向倾斜。CCP和CRP原则其实也存在对抗关系,是最小可用原则和保持更多可选项观点间的对抗,我们要具体针对项目工期,公司资源,团队大小,项目紧急程度来进行权衡。

 

 

 

组件耦合原则

 

该原则告诉我们管理组件之间的关系

 

 

 

无环依赖原则

 

组件依赖关系图中不应该出现环

 

程序员总希望可以单打独斗,可大多数情况下我们需要与其他人共同协作开发维护软件,当其他人的修改,很可能会影响到自己的功能。一觉醒来,我的功能却不能用了!

 

解决方案:

每周构建:周一到周四大家在自己分支上开发自己的功能,周五进行统一构建

好处:每周都有四天可以让大家放手工作

坏处:将风险延后到星期五,可能会频繁导致周六日加班

 

 

消除循环依赖:项目划分为一些组件,被单独负责,当组件变更,更改版本号后通知出来。其他依赖项目选择在合适的时机升级验证。

好处:组件变更不会立刻影响到其他团队,减少团队间的直接依赖,使得集成工作以一种小型渐进的方式进行。

坏处:项目架构难度高,需要控制好组件之间的依赖结构,消除依赖环。

 

 

如何消除依赖环:

依赖倒置:反转组件间的依赖关系

创建新组件:将两个组件相互依赖的部分提取出来,创建一个新的组件

 

 

 

组件结构图不是自上而下被设计出来的,他是当前项目真实状态的地图,随着项目变更不断的演变,不是项目的设计图,没办法在项目初期就确定下来。它的一个重要目标是隔离变化,识别出稳定高价值组件与易变组件,将它们隔离起到保护作用。

 

 

 

 

 

依赖倒置

面向过程的开发,上层调用下层,上层依赖于下层,当下层剧烈变动时上层也要跟着变动,这就会导致模块的复用性降低而且大大提高了开发的成本。

面向对象的开发很好的解决了这个问题,一般情况下抽象的变化概率很小,让用户程序依赖于抽象,实现的细节也依赖于抽象。即使实现细节不断变动,只要抽象不变,客户程序就不需要变化。这大大降低了客户程序与实现细节的耦合度。

 

前端组件化_第8张图片 ---> 前端组件化_第9张图片

 

 

 

 

稳定依赖原则

依赖关系必须要指向更稳定的方向,确保设计中容易变更的模块,不会被难以修改的模块所依赖

 

 

稳定性:只需要很小成本就可以改变状态是不稳定的,需要成本很高,那它是稳定的

 

不稳定性指标:依赖组件数/(依赖组件数+被依赖组件数)

 

 

 

 

稳定抽象原则

 

一个组件抽象化程度应该与其稳定性保持一致

 

设计出哪些组件应该稳定,哪些组件应该不稳定

 

稳定的组件很难被修改,为了让组件既稳定又可以被修改,我们一般应用开闭原则,让组件保持一定的抽象,可以被扩展。

 

抽象程度:总类/抽象类

 

 

 

 

模块间通信

 

 

传统方式

 

好处

  • 开发代码可读性高
  • 开发效率高
  • 性能更高
  • 代码量更小

 

坏处

  • 耦合度高、不易维护
  • 不易扩展
  • 维护代码可读性低
  • 难以复用

 

 

示意图

 

前端组件化_第10张图片

 

 

 

 

事件总线

组件间进行通信的桥梁,附载到总线上的组件都可以相互通信,所以使用简单,可以很方便将消息从一个组件发送到另外一个组件。虽然这种方式使组件间解耦,但是大量使用必定造成在维护阶段代码难以维护。事件总线是一种一对多,或者多对多的关系,类似于广播,也是观察者模式的一种应用,在实际应用中不建议应用于组件间一对一的通信方式。

 

好处

  • 解耦合
  • 实现简单
  • 通信方便

 

 

坏处

  • 可读性降低
  • 不可跟踪代码
  • 侵入性高

 

 

示意图

 

前端组件化_第11张图片

前端组件化_第12张图片前端组件化_第13张图片前端组件化_第14张图片

 

 

全局状态管理

 

帮助中大型单页应用,更好地管理组件外部状态,适用于一个组件影响另外一个组件数据的情况。

 

好处

  • 代码结构化
  • 易维护
  • 降低大型模块开发成本

 

坏处

  • 增加复杂度
  • 降低可读性
  • 只可以更改值,不可以触发事件
  • 未能完全降低耦合度

 

 

示意图

 

前端组件化_第15张图片

 

 

 

 

父组件调用

 

好处

  • 组件间耦合度低
  • 组件结构化
  • 子组件可测试
  • 可读性高、易维护

 

 

坏处

  • 组件要求高,开发难度大
  • 嵌套结构深的情况下,难以维护

 

 

示意图

 

前端组件化_第16张图片

 

 

 

总结

父子组件通信、最小颗粒度组件内部通信使用传统方式,直接进行访问。

直接兄弟组件间通信,通过父组件进行调用

非直接兄弟组件间通信,通过全局状态管理

跨页面级组件间通信,通过事件总线

 

 

 

软件架构

 

将系统切分为组件,并且安排好组件间的依赖关系,及通信方式。并且在设计中尽可能长时间的保留更多的可选项。

 

 

针对不同的团队,采用不同的架构,提高团队开发效率,降低团队维护发布成本。架构在其中起到协调作用,在开发效率与维护成本之间做取舍。

 

软件架构的主要目标是支撑系统的全生命周期,设计良好的架构可以让系统更利于理解,易于修改,方便维护,并且能轻松部署。终极目标就是最大程序员生产力,最小系统运营成本。

 

设计良好的架构可以帮助程序员更容易理解系统的运行过程,间接的帮助对系统的开发和维护。

 

设计良好的架构可以降低修改对其他部分的影响,降低实际的维护成本。

 

设计良好的架构可以使软件保持更好的灵活性,通常保持更多的可选项目,可以使得项目变更时降低成本。

 

我们的终极目标就是以最小的人力成本来构建和维护软件系统

 

 

 

 

价值纬度

 

软件系统应该从两个纬度来衡量它的价值,行为纬度和架构纬度。

 

行为价值:我们将产品文档翻译成具体的代码,使得程序可以按照文档描述运行起来,并且出现bug时候要进行调试修复。这是最直观的价值,且大部分人认为这是他们全部的工作。

 

架构价值:软件系统必须保持一定的灵活性,使其更容易被修改,这体现了软件上软的价值,而在机器上不容易被改变的部分,我们称为硬件。

 

哪个纬度更重要呢?通常人们可能会认为行为价值更为重要,一个软件系统如果不能正常的运行起来,那他的价值应该为0。但是对于一个仅仅在当前能够运行起来,但是却无法被修改的系统来说是无法持续产生价值的。相对应的,对于一个暂时无法运行,但是很容易被修改的系统而言,让它能够正常工作,应该是一件简单的事情。

 

行为价值是紧急但不重要的

 

架构价值是重要但不紧急

 

 

 

六大设计原则

 

 

单一职责原则(SRP)

 

一个类被改变的原因只有一个,一个类只有一个职责可以提高类的可读性和可维护性,并且降低变更风险。如何划分职责,我们还要从需求和业务出发,识别出同一类型的职责。

 

 

开闭原则(OCP)

 

对扩展开放,对修改关闭,是进行软件架构设计的指导原则,其主要目的是让系统便于扩展,同时限制每次修改影响的范围,实现方式是通过将系统划分为一系列的组件,并且组织依赖关系,使得高阶组件不会因为低阶组件的修改而收到影响。

 

 

里式替换原则(LSP)

 

任何基类出现的地方,一定可以被子类所替换,是指导接口与具体实现关系的设计原则。该原则一般被应用在软件架构层面,一旦违反了该原则,架构设计中就不得不增加大量复杂的应对机制

 

 

接口隔离原则(ISP)

 

避免使用不需要的接口,类间依赖关系建立在最小的接口上,所以也叫做最小接口原则。ISP是单一职责原则在接口层面的体现,避免他的实现类破坏单一职责原则,提高其内聚性。ISP要求我们将接口设计的足够小,足够灵活,但是也不是越小越好,接口越小灵活度越高,但是系统复杂度也会越高,所以接口不是越小越好必须要有一个度。

 

 

 

依赖反转原则(DIP)

 

上层模块不依赖底层模块依赖抽象,抽象不依赖细节,细节依赖抽象,针对接口编程。

 

在面向过程的开发中,依赖关系总是自上而下,上层模块依赖和调用下层模块,当下层模块剧烈变更时,上层模块总是会跟着改动,变化会从下向上传递。

在面向对象的开发中为了解决这个问题,遵循依赖倒置原则,构建稳定的抽象层,当细节变更了,也不容易引起抽象层的变更,这样客户端更稳定。

 

 

迪米特法则(LKP)

 

它要求对其他对象有最少的了解,所以又叫做“最小知道原则”,它主要的意义在于降低类之间的耦合,使系统模块更容易独立开来,只要类之间关系低耦合,才可以增强类的复用,降低类变更的分险。

怎么做到最小知道?1:只和直接的朋友(成员变量,方法的输入输出参数)交流,2:减少对朋友的了解。

迪米特法则目的把类编程一个肥宅,减少与外接的交流,限制类的活动范围。

设计模式中,门面模式和中介模式是实现迪米特法则的常见手段。

你可能感兴趣的:(前端组件化)