[微前端] 前端架构历史简介与设计原则
前端架构历史简介
初始无架构
前端代码内嵌到后端应用中
后端 mvc 架构
将视图层, 数据层, 控制层做分离
缺点: 重度依赖开发环境, 代码混淆严重. 比如前端调试时, 需要启动和安装后端所有依赖, 从本地调试. 非常复杂. 很多依赖通过后端服务注入.
前后端分离架构
将前端代码从后端环境中提炼出来(ajax 促进了前后端分离架构的发展)多页面架构
缺点:
- 前端缺乏独立部署能力, 整体流程依赖后端环境
发展:
Nodejs 发展诞生了多元化前端开发方式, 使得前端开发可以脱离整体后端环境
单页面架构
打包: gulp, rollup, webpack, vite..
框架: react/vue/angular..
ui 库: antd/iview/elementui/mintui...
优势:
切换页面无刷新浏览器, 用户体验好
组件化开发方式
缺点:
不利于 SEO, 首次渲染可能会较长时间白屏
大前端
后端框架: express, koa
包管理: npm, yarn
node 版本管理: nvm
微前端
技术栈无关
独立开发独立部署
支持增量升级
微前端是一种非常好的实施渐进式重构的手段和策略
微应用仓库独立, 前后端可独立开发, 主框架自动完成同步更新
缺点:
接入难度高
应用场景: 移动端少, 多在 PC 端管理后台上应用
总结
过于灵活的实现也导致了前端应用拆分过多, 维护困难
往往一个功能或需求会跨两三个项目进行开发
软件设计原则 SOLID
单一职责原则
概念: 永远不应该有多于一个原因来改变某个类
理解: 对于一个类而言, 应该仅有一个引起它变化的原因
应用: 如果一个类拥有了两种职责, 那就可以将这个类分成两个类
例子
登录页面: 有两个验证功能, 1. 验证用户名 2. 验证密码
如果两个功能放在同一个方法里, 不仅不利于查找错误原因, 而且一个方法里会有很多的逻辑判断, 加重开发负担与维护困难
所以可以将两个验证功能拆分为两个方法去实现
开放封闭原则
概念: 软件实体扩展应该是开放的, 但对于修改应该是封闭的
理解: 开发过程中, 对扩展开放, 对修改封闭. 可以去扩展类, 但不要去修改类
应用: 当需求有改动, 尽量用继承或组合的方式来扩展类的功能, 而不是直接修改类的代码
例子
登录页面: 有两个验证功能, 1. 验证用户名 2. 验证密码
现在要增加一个验证码功能, 只有通过图片验证码之后, 才可以验证密码.
此时如果直接取修改验证密码函数的功能, 那么要做出大量修改, 而且需要测试, 并且也会对原有功能产生影响.
可以通过增加一个函数, 通过这个函数返回 true 或 false 的结果判断是否进行接下来的流程. 这样仅会影响图片验证码功能
里氏替换原则
概念: 父类一定能够被子类替换
理解: 在使用父类的方法的时候, 如果将父类的引用换为子类的引用, 那么对于整体的程序设计来说是没有任何影响的. 里氏替换原则在使用面向对象的时候不会着重的注意到. 但是现在在使用函数式编程的时候可能关注度并没有很高
最少知识原则
概念: 只与你最直接的对象交流
理解: 低耦合, 高内聚
应用: 做系统设计时, 尽量减小依赖关系
例子
上个图片验证码的功能与验证用户名和密码功能是强依赖相关的. 因为需要知道验证码通过与否来判断是否验证用户名和密码. 那么针对其他依赖项, 只有图片验证码功能是强相关, 所以只需要知道验证码结果就可以进行用户名和密码校验
接口隔离原则
概念: 一个类与另一个类之间的依赖性, 应该依赖于尽可能小的接口
理解: 一个接口不要对外暴露没有实际意义的接口. 用户不应该依赖它不需要的接口
应用: 当需要对外暴露接口时, 如果是非必要对外提供, 尽量删除
例子
如果说登录验证用户名和密码接口是必须的, 但是跟用户所处的地域环境, 比如说 ip 没有关联. 那么就不需要对这样一个接口做依赖, 而只暴露验证用户名和密码接口即可
接口: 这里的接口并不是日常获取数据的接口, 而是在程序设计中通过接口去预设一部分类型, 来限定一个类是怎样实现的这个接口
依赖倒置原则
概念: 高层模块不应该依赖于底层模块, 他们应该依赖于抽象, 抽象不应该依赖于细节, 细节应该依赖于抽象
理解: 应该面向接口编程, 不应该面向实现类编程. 并不是说, 所有的类都要有一个对应的接口, 而是说, 如果有接口, 那就尽量使用接口来编程吧
总结
将以上六大原则的英文首字母拼在一起就是 SOLID, 所以也称为 SOLID 原则
补充设计原则
组合/聚合复用原则
当要扩展类的功能时, 优先考虑使用组合, 而不是继承
该原则在 23 种经典设计模式中频繁使用. 如代理模式, 装饰模式, 适配器模式等
无环依赖原则
- 当 A 模块依赖于 B 模块, B 模块依赖于 C 模块, C 依赖于 A 模块, 此时将出现循环依赖. 在设计中要避免该问题. 可通过引入中介者模式解决
共同封装原则
应该将易变的类放在同一个包里, 将变化隔离出来. 该原则是开放-封闭原则的诞生
共同重用原则
如果重用了包中的一个类, 那么也就相当于重用了包中的所有类, 我们要尽可能减小包的大小
好莱坞原则
Dont call me, i will call you
控制反转, 或称为依赖注入
不需要主动创建对象, 而是由容器帮我们来创建并管理这些对象
不要重复你自己
不要让重复的代码到处都是, 要让他们足够的重用, 所以要尽可能的封装
保持他简单与傻瓜
保持系统界面简洁, 功能实用, 操作方便
高内聚与低耦合
模块内部需要做到内聚度高, 模块之间需要做到耦合度低
关注点分离
将一个复杂的问题分离为多个简单的问题, 然后逐个解决
你不需要它
不要一开始就把系统设计的非常复杂, 不要陷入过度设计的深渊, 让系统足够简单, 而又不失扩展性
软件设计分层
架构种类: 系统级架构, 应用级架构, 模块级架构, 代码级架构
系统级架构
应用在整个系统内, 如与后台服务如何通信, 与第三方系统如何集成
设计前端首要条件: 了解前端系统与其他系统之间的关系
关系包括: 业务关系和协作机制
设计后端: 只需要规定与后台数据传递的机制.
包括: api 设计原则, 访问授权的一个开放标准(Oauth)跳转 token 的验证, 数据传递 cookie 等
前端与后端的关系考虑的主要因素是: 前后端分离的架构设计
前后端分离架构其实是如何实施技术决策, 用户鉴权, API 接口管理和设计, API 文档管理, Mock 的使用, BFF(服务于前端的后端, nodejs), 是否需要服务端渲染, 应用间分层, 性能优化, 代码拆分, 项目管理等等这些内容
关于微前端的补充
在一个系统内微前端是应用间的架构方案. 也就是说微前端整体的子应用是应用在一个系统之内的, 但是对于整体的应用, 多个应用而言, 微前端则是一种系统间等架构方案. 微前端负责的是调度所有的子应用. 不管是每个子应用的渲染, 数据依赖和更新获取, 子应用的激活与卸载等等所有内容都是由微前端框架去完成的. 总体而言, 微前端是将多个前端应用以某种形式结合在一起进行应用
微前端解决的实际问题
旨在解决单体应用在一个相对长的时间跨度下, 由于参与的人员, 团队的增多变迁, 从一个普通应用演变成一个巨石应用(Frontend Monolith)后, 随之而来的应用不可维护的问题
微前端的实现形式
单实例形式: 即同一时刻, 只有一个子应用被展示, 子应用具备一个完整的应用生命周期. 其他子应用处于未激活状态
多实例形式: 同一时刻可展示多个子应用.通常基于 url 的变化来做子应用的切换. 通常使用 Web Components 方案来做子应用封装, 子应用更像是一个业务组件而不是应用
应用级架构
应用级架构可以看做是整体系统级架构的细化
单个应用于其他外部应用的关系, 微服务架构下多个应用的协作, 数据交换等
应用级架构涉及几种形式: 应用间脚手架, 模式库, 设计系统
模块级架构
这部分内容使我们开始业务编码之前进行设计, 我们称为迭代
代码级架构
- 规范与原则, 开发流程, 代码质量以及改善, 规范而非默契
如何保证架构质量
系统的稳定性
定义: 当一个实际的系统处于一个平衡的状态时, 如果受到外来作用的影响时, 系统经过一个过渡过程仍然能回到原来的平衡状态, 我们称这个系统就是稳定的, 否则不稳定
架构设计的基石, 可以更好的实现自我修复
系统的健壮性
定义: 计算机软件在输入错误, 磁盘故障, 网络过载或有意攻击情况下, 能否不死机, 不崩溃, 就是该软件健壮性的具体表现.
解释: 一个系统容错能力强, 运行不易被干扰, 安全性好
度量标准: 一个软件可以从错误的输入推断出正确合理的输入, 一个软件可以正确的运行在不同环境下, 一个软件能够检测自己内部的设计或者编码错误, 并得到正确的结果
架构质量的衡量
拓展性, 可管理, 维护性, 高可用(故障修复, 容灾, 降级, 熔断)
日常开发过程中的架构质量指标
理解难度, 接入依赖的成本, 崩溃率和错误率的指标, 开发效率, 错误上报和信息收集等功能