微前端

一、微前端的概念

将前端应用分解成一些更小、更简单的能够独立开发、测试、部署的小块,而在用户看来仍然是内聚的单个产品。

二、微前端的特点

简单来讲,微前端的理念类似于微服务:

将庞大的整体拆成可控的小块,并明确它们之间的依赖关系。关键优势在于:

  • 技术栈无关

主框架不限制接入应用的技术栈,微应用具备完全自主权

  • 独立开发、独立部署

微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 增量升级

在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

  • 独立运行时

每个微应用之间状态隔离,运行时状态不共享

1.简单、松耦合的代码库

比起一整块的前端代码库,微前端架构下的代码库倾向于更小/简单、更容易开发。此外,更重要的是避免模块间不合理的隐式耦合造成的复杂度上升。通过界定清晰的应用边界来降低意外耦合的可能性,增加子应用间逻辑耦合的成本,促使开发者明确数据和事件在应用程序中的流向

2.增量升级

为了实施渐进式重构,我们需要一种增量升级的能力,先让新旧代码和谐共存,再逐步转化旧代码,直到整个重构完成。

这种增量升级的能力意味着我们能够对产品功能进行低风险的局部替换,包括升级依赖项、更替架构、UI 改版等。
另一方面,也带来了技术选型上的灵活性,有助于新技术、新交互模式的实验性试错。

3.独立部署

独立部署的能力在微前端体系中至关重要,能够缩小变更范围,进而降低相关风险。

4.团队自治

除代码库及发布周期上的解耦之外,微前端还有助于形成完全独立的团队,由不同团队各自负责一块产品功能从构思到发布的整个过程,团队能够完全拥有为客户提供价值所需的一切,从而快速高效地运转。

为此,应该围绕业务功能纵向组建团队,而不是基于技术职能划分。最简单的,可以根据最终用户所能看到的内容来划分,比如将应用中的每个页面作为一个微前端,并交给一个团队全权负责。与基于技术职能或横向关注点(如样式、表单、校验等)组织的团队相比,这种方式能够提升团队工作的凝聚力。

三.实现方案

实现上,关键问题在于:

  • 多个 Bundle 如何集成?
  • 子应用之间怎样隔离影响?
  • 公共资源如何复用?
  • 子应用间怎样通信?
  • 如何测试?

1.多 Bundle 集成

微前端架构中一般会有个容器应用(container application)将各子应用集成起来,职责如下:

渲染公共的页面元素,比如 header、footer
解决横切关注点(cross-cutting concerns),如身份验证和导航
将各个微前端整合到一个页面上,并控制微前端的渲染区域和时机
集成方式分为 3 类:

  • 服务端集成:如 SSR 拼装模板
  • 构建时集成:如 Code Splitting
  • 运行时集成:如通过 iframe、JS、Web Components 等方式

2.服务端集成

服务端集成的关键在于如何保证各部分模板(各个微前端)能够独立发布,必要的话,甚至可以在服务端也建立一套与前端相对应的结构:

每个子服务负责渲染并服务于对应的微前端,主服务向各个子服务发起请求

3.构建时集成

常见的构建时集成方式是将子应用发布成独立的 npm 包,共同作为主应用的依赖项,构建生成一个供部署的 JS Bundle

然而,构建时集成最大的问题是会在发布阶段造成耦合,任何一个子应用有变更,都要整个重新编译,意味着对于产品局部的小改动也要发布一个新版本,因此,不推荐这种方式。

4.运行时集成

将集成时机从构建时推迟到运行时,就能避免发布阶段的耦合。常见的运行时集成方式有:

  1. iframe JS:比如前端路由 Web Components 虽然直觉上用 iframe
    好像不太好(性能、通信成本等),但在这里确实是个合理选项,因为 iframe 无疑是最简单的方式;

  2. 天然支持样式隔离以及全局变量隔离:但这种原生的隔离性,意味着很难把应用的各个部分联系到一起,路由控制、历史栈管理、深度链接(deep-linking)、响应式布局等都变得异常复杂,因而限制了iframe 方案的灵活性

  3. 前端路由:每个子应用暴露出渲染函数,主应用在启动时加载各个子应用的独立Bundle,之后根据路由规则渲染相应的子应用。目前看来,是最灵活的方式

  4. Web Components:将每个子应用封装成自定义 HTML 元素(而不是前端路由方案中的渲染函数),以获得Shadow DOM带来的样式隔离等好处

5.影响隔离

子应用之间,以及子应用与主应用间的样式、作用域隔离是必须要考虑的问题,常见解决方案如下:

样式隔离:开发规范(如BEM)、CSS 预处理(如SASS)、模块定义(如CSS Module)、用 JS 来写(CSS-in-JS)、以及shadow DOM特性作用域隔离:各种模块定义(如ES Module、AMD、Common Module、UMD)

6.资源复用

资源复用对于 UI 一致性和代码复用有重要意义,但并非所有的可复用资源(如组件)都必须在一开始就提出来复用,建议的做法是前期允许一定程度的冗余,各个 Bundle 在各自的代码库中创建组件,直到形成相对明确的组件 API 时再建立可供复用的公共组件

另一方面,资源分为以下 3 类:

基础资源:完全不含逻辑功能的图标、标签、按钮等
UI 组件:含有一定 UI 逻辑的搜索框(如自动完成)、表格(如排序、筛选、分页)等;

业务组件:含有业务逻辑,其中,不建议跨子应用复用业务组件,因为会造成高度耦合,增加变更成本;

对于公共资源的归属和管理,一般有两种模式:

  • 公共资源归属于所有人,即没有明确归属
  • 公共资源归集中管理,由专人负责

从实践经验来看,前者很容易衍变成没有明确规范,且背离技术愿景的大杂烩,而后者会造成资源创建和使用的脱节,比较推荐的模式是开源软件的管理模式:

即,所有人都能补充公共资源,但要有人(或一个团队)负责监管,以保证质量、一致性以及正确性。

7.应用间通信

通过自定义事件间接通信是一种避免直接耦合的常用方式,此外,React 的单向数据流模型也能让依赖关系更加明确,对应到微前端中,从容器应用向子应用传递数据与回调函数

8.测试

每个子应用都应该有自己的全套测试方案,特殊之处在于,除单元测试、功能测试外,还要有集成测试:

集成测试:保证子应用间集成的正确性,比如跨子应用的交互操作
功能测试:保证页面组装的正确性
单元测试:保证底层业务逻辑和渲染逻辑的正确性
自下而上形成一个金字塔结构,每一层只需验证在其下层覆盖不到的部分即可

四.示例

在线 Demo:https://demo.microfrontends.com/
源码地址:micro-frontends-demo/container
详细介绍:The example in detail

五.缺点

当然,这种架构模式并非百益而无一害,一些问题也随之而来:

  • 导致依赖项冗余
  • 增加用户的流量负担
  • 团队自治程度的增加
  • 可能会破坏协作

六.总结

类似于微服务之于后端,前端业务在发展到一定规模之后,也需要一种用来分解复杂度的架构模式,于是出现了微服务思想在前端领域的应用,即微前端。主要目的在于:

  • 技术架构上进一步的扩展性(模块边界清晰、依赖明确)
  • 团队组织上的自治权
  • 开发流程上能独立开发、独立交付
  • 最大的意义在于解锁了多技术栈并存的能力,尤其适用于渐进式重构中架构升级过渡期
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值