写点什么

一个渐进式微前端框架 - Fronts

  • 2021 年 12 月 09 日
  • 本文字数:4967 字

    阅读完需:约 16 分钟

作者介绍—Michael

RingCentral Integration Frontend Tech Leader

曾担任若干公司前端负责人,擅长前端架构设计与 JavaScript 通用化,

对前端框架设计与自动化测试框架设计有深入研究及实战经验。

R/什么是微前端

微前端是一种前端架构风格,它提倡由可独立交付(开发/部署)的前端应用组合成一个更大的整体前端应用。


随着前端工程复杂度逐年上升,传统大型前端应可能因过度耦合变得越来越臃肿而最终难以维护,也因此在近两三年的大型前端架构中,微前端也越来越受到重视。前端应用模块动态化无疑是一个不可避免的前端开发新趋势之一,它将有可能较彻底地解决臃肿代码库维护问题和效率不高的交付问题。


R/微前端的价值

  • 独立与自治

只有在应用开发整体流程中能独立开发、独立部署和独立自治代码库等,那么这个前端项目才能具有真正的独立性保证;而这种团队自治的可能性也恰好因契合了 Conway's Law 提到的“设计系统的架构受制于产生这些设计的组织的沟通结构”, 从而也带来了可能的组织管理形式新变革。


  • 技术栈无限定

微前端的技术栈自主性有利于多个不同技术栈的团队协同合作;同时技术栈的可平滑迁移也对旧有业务的不断迭代和技术升级带来较大的便利性。


  • 运行时集成

在现代前端开发流程中,我们最常见的往往是构建时的复用集成;反倒是前端早期时,运行时复用的模块分离得更独立;而微前端也恰好能整合好这样的微模块概念,并保持这样模块的独立性和依赖共享。


  • 颗粒化解耦与可组合

在大型的前端工程中,我们对于颗粒化解耦有很高的要求,常基于不同纬度划分,例如业务类型颗粒化、技术服务类型颗粒化等等。各个微前端颗粒的可组合性又让多个可交付系列产品有很好的颗粒一致性和整体定制差异化,并能极大减少业务重复开发的资源浪费。


总体来说,恰当地运用并落地微前端架构,将对一些大型前端工程的长期维护带来深远的价值。微前端可以带来更高效的交付效率和大型开发团队内的分治;“技术栈无限定”也带来各种对微前端颗粒平滑渐进式升级带来可能,同时也让各种技术栈的创新演进,并在实际的各个业务上落地变得更具有可行性;“微前端颗粒化“带来了可能的版本控制诉求,它带给我们对于微前端颗粒间依赖管理与版本管理更确切的思路。


R/动机

在众多微前端解决方案中,single-spa 和 Module Federation 是其中的佼佼者。


single-spa 是一个基于以路由配置为主要特点的微前端框架,由微前端配置的中心化带来了一系列限制,诸如较难以颗粒化可嵌套的微前端、弱化模块颗粒度可控、模块共享等,而它以及众多基于它研发的微前端解决方案或多或少都难以摆脱以路由为中心的限制。


2019 年,Zack Jackson 提出并实现 Module Federation,它的概念与 single-spa 完全不同,Module Federation 在构建时处理共享模块,它允许在前端应用程序运行时从另一个应用程序资源动态加载代码并共享依赖。Module Federation 彻底解决了代码依赖共享问题与微前端运行时模块化问题。这一构想确实也如 Zack Jackson 文章提到的它是"A game-changer in JavaScript architecture"。Module Federation 让前端运行时第一次有了和 Node.js 的模块系统有类似设计的可能。Module Federation 目前已经被 Webpack,next.js 和 rollup 所支持。


尽管 Module Federation 概念如此惊艳,但是它毕竟尚未进一步形成一个完整并完全针对微前端的框架设计与实现,而这就是 Fronts 框架所要做的,并尝试解决一些目前微前端领域中一些热点争议问题。


R/微前端框架的争议点

基于目前主流的微前端框架或概念,以下整理了主要涉及的争论点:


1.颗粒级别划分应该是应用级别还是模块级别

从灵活性和颗粒度来说,模块级别显然更具有优势,但是为了兼容一些并不是那么现代的前端工程,支持应用级别显然也有一定的优势,因此我们需要一个两者都能支持框架。如果需要应用级别的运行时集成,显然仅仅只是使用 Module Federation 的 Webpack 是不够的,我们还需要一个运行时的应用级别颗粒入口点加载器。


2.入口点是 HTML 文件还是 JS 文件为主

从现代工程角度而言,前端应用入口点大多以 JS 为主,而早中期的一些前端工程也不乏仅以 HTML 为入口点,这里有利弊权衡,HTML 为主要入口点的应用来说,建构一个微前端体系,那它必然是一个异步请求(甚至是跨域请求),并能解析其 DOM 结构,再进行一些主动的拆解整理与渲染运行,甚至加入运行时 Sandbox,但这一整体处理流程稍显冗长与繁复,这种拆解库更适合是一个独立的非核心工具包,而整体的框架应该以 JS 文件为入口点。


3. 除了支持入口点,是否有必要支持其非入口点的模块共享

模块共享是微前端的必解之题,否则运行时臃肿与重复的各种资源浪费,将让微前端的落地意义大打折扣。但在早中期的前端模块中,共享依赖主要通过 script 的 HTML 标签来相对静态方式管理模块,而目前也只有 Module Federation 的 Webpack 则让这样的模块共享分离在构建时处理完成,并能进行运行时动态依赖共享。除了 Module Federation,目前还尚未出现其他更完美的解决方案。


4. CSS/JS 隔离解决方案成本利弊选择

CSS 的隔离是在所难免,这也被不少微前端框架所支持。而 JS 隔离实现成本相对就高了不少,需要异步请求到这个 JS 文件(甚至是跨域),并针对各种可能的访问的全局接口进行全面的劫持代理,既要保证安全性、性能和稳定性,还要考虑不同浏览器的兼容性等方面,其实往往得不偿失,尤其是现代前端工程中甚少全局污染,事实上这样的隔离是否是非常必要,也取决于落地在每个微前端颗粒中的实际情况。如果是外部不可控风险的颗粒,我们往往可以少量地使用 iFrame 来做到真正的 sandbox 隔离,通过约定接口的方式进行交互。而在大多数可控的微前端颗粒中,事实上我们仅仅需要非隔离方式进行在同一个 JS 运行容器上直接地相互访问,这是高效便捷的。


5. 微前端颗粒的通用性,以及是否需要支持多运行容器与多模式,SSR 等

在大型前端工程中,往往不会只是以构建一个 Web 应用这样的形式存在,而可能是构建多个 Web 应用,甚至是更多中前端应用类型,例如 Electron 应用、浏览器插件、小程序或移动端应用等。因此一个好的微前端框架应该能够兼容更多种运行容器和打包成多种应用类型,也最好能兼容构建单体应用和微前端应用。Module Federation 也初步实现了在 next.js 实现 SSR 的支持。


6. 版本控制和依赖管理

在大型的成熟前端应用中,不断的快速迭代和业务膨胀,因此各种大小粒度的模块管理变得非常重要,因此当一个大型前端工程试图开始引入微前端架构后,往往演化到后期,版本控制和依赖管理将变得尤为重要,它将很大程度上提升了大型前端工程的交付管理和可维护性。


为了解决以上争议,Fronts 应运而生。


R/Fronts 是什么

Fronts 是一个基于 Webpack 的 Module Federation API 设计的渐进式微前端框架。它强调颗粒间的去中心化依赖管理,并支持多种运行模式来满足不同的微前端架构需求。


  • 支持非 Module Federation - 虽然 Fronts 基于 Module Federation 概念, 但它依然支持任何传统且不支持 Module Federation 的前端应用。

  • 去中心化配置 - 只需要在每个 Fronts 应用中设置 site.json,就像设置一个 package.json 一样简单,Fronts 支持多层嵌套的微前端。

  • 跨框架 - 没有任何现代前端框架限定。

  • 代码分割/懒加载 - 支持在 Fronts 应用内进行代码拆分和导出共享模块,它可以被其他 Fronts 应用作为依赖模块进行懒加载。

  • CSS 隔离 - 可选的 CSS 隔离设定,并根据不同的渲染方式有宽松隔离和严格隔离的可选项。

  • 生命周期 - 每个 Fronts 应用的 Entry 支持简洁的生命周期接口。

  • Web Components 和 iFrame - 支持多种前端运行时容器用于不同隔离环境要求。

  • 多种构建模式 - 同时支持在微前端模式和非微前端模式构建,兼容动态化的运行时集成和静态化的构建时集成。

  • Monorepo 和 TypeScript - 良好支持 Monorepo 和 TypeScript,它们和 Fronts 是非常适合架构在一起的技术栈。

  • 版本控制 - Fronts 提供的版本控制可用于高效和动态的即时交付应用,当然也包括支持灰度发布。

  • 零劫持 - Fronts 不做任何执行容器上的环境全局公共 API 劫持, 保持运行环境的原始性,避免可能带来性能损失和安全问题。

  • 通用化消息通讯 - Fronts 提供简洁通用的响应式 API, 它支持前端绝大部分原生 API。


R/Fronts 的特点是什么

Fronts 是一个简单易懂的微前端框架。


通过设置 site.json 来定义一个微前端,相当于前端运行时的一个 package.json 。

Fronts 是渐进式的。


如果前端工程的各个颗粒并不都支持 Module Federation,它依然能以微前端的形式良好地运行,也能自由选择所需要的运行模式;并随着项目升级,可以逐一升级到支持 Module Federation,并最终根据实际需要建立和开启微前端版本控制系统。Fronts 所支持多种粒度级别、构建类型、模块共享类型、运行类型和通讯类型等各种维度,它的丰富可架构性几乎能满足各种微前端架构。


Fronts 的 API 是简洁的。


Fronts 提供了 useApp( ), useWebComponents( )和 useIframe( )这三组加载器,并额外提供一个 Entry 的启动器 boot( )和 Webpack 配置生成器 createWebpackConfig( )。通过这些 API,你将可以快速高效地进行微前端开发。


R/一个简单的例子

在该例子中,我们将基于 Fronts 搭建一个微前端项目,其中 App1 作为主要的 App Shell 入口,并且它将依赖 App2。


你可以根据 Webpack 官方文档教程搭建 app1 和 app2 两个 React 项目。篇幅有限,这里假设你已经完成了这两个 Webpack5 构建的 React 项目基本搭建, 现在让我们快速体验一下 Fronts 微前端开发之旅。


1.安装两个项目中分别安装 fronts-react and fronts-bundler


2.分别在两个项目中设置 site.json 和修改 webpack.config.js

这里假定 app1 依赖 app2:

app1/site.json:


app2 没有其他依赖微前端颗粒, 并同时定义导出./src/bootstrap 它将作为 app2 微前端的入口点被 app1 所以使用;导出./src/Button 作为一个 app2 的一个外部共享模块组件。


app2/site.json:


在两个项目中,我们都使用 createWebpackConfig( )API 传入 webpack.config.js 中的原有配置并导出。


3. 在 app1/src/App.tsx 中使用 useApp( )载入被导出的 app2/src/bootstrap 的应用入口点。


4. 在 app2/src/bootstrap.tsx 中定义默认导出的启动函数并使用 boot( )将它启动。


这时候同时运行 yarn start, 我们将看到 app1 和 app2 两个项目同时运行,并且 app2 被作为一个微前端颗粒渲染在 app1 当中,而 app2 的 Button 也被 app1 当成一个共享的模块组件。


相关的例子源代码可移步这里:

https://github.com/unadlib/fronts-example


R/一些补充说明

  • 内建包

目前主流前端框架依然是 React,Vue 和 Angular,当落地的微前端颗粒刚好是这其中的一个框架时,推荐使用 Fronts 针对这个框架的内建包,例如 fronts-react、fronts-vue 和 fronts-ng, 而且当遇到不在内建包支持的其他框架或无框架,那么请使用`front`中的 API。


  • 内建包 API

每个内建包都包含 useApp( ), useWebComponents( ), useIframe( )这三组加载器。useApp( )提供宽松的 CSS 隔离,useWebComponents( )提供严格的 CSS 隔离,useIframe( )提供原生严格的 CSS 隔离和 JS 隔离,在实际使用中可根据各个微前端之间的信任关系选用。


  • 版本控制

Fronts 尚在初步开发阶段,并未完成整体版本控制的套件支持,目前仅支持自行搭建的 Registry Server。


  • Monorepo 和 TypeScript

大型前端工程往往意味着复杂度高,因此 Fronts 非常适合运用在 Monorepo 和 TypeScript 这样的技术栈组合中,它在类型安全和代码管理上将和运行时集成一起形成一个良好的维护体验。而且仅当全部微前端颗粒均在 Monorepo 的子包时,将 yarn start 变成 SPA=true yarn start 就能将微前端开发模式切换成传统 SPA 的开发模式。


R/结论

基于 Fronts/Monorepo/TypeScript 的前端架构,最终将在代码管理、类型安全、业务开发分治和部署快速交付上的效率有显著提升,并实现产品业务组合的灵活性,业务代码的高复用和一致性,以及产品形式的多样性。


每个试图落地微前端架构的大型前端工程都有各自不同或相似的需求点,因此通过分析自身大型前端工程的诉求和需求,并以此去建立或选择自己的微前端落地架构才有可能真正解决自身亟待解决的工程痛点。Fronts 试图以基于 Module Federation 的通用模块概念去更针对性地系统解决微前端的主要可能遇到的痛点,诸如跨框架、依赖共享、依赖管理、兼容多运行时容器与模式等等。


Fronts 希望从更广泛的微前端落地需求去逐步演进成一个可靠和高效的微前端框架。

发布于: 3 小时前阅读数: 16
用户头像

还未添加个人签名 2021.11.24 加入

全球云商务通信与协作解决方案领导者,连续七年荣膺Gartner UCaaS(统一通信即服务)魔力象限全球领导者。与你分享各种技术专家的文章、公开课,各种好玩有趣的活动与福利,以及最新的招聘机会。

评论

发布
暂无评论
一个渐进式微前端框架 - Fronts