我们可以采用微服务架构模式并将其应用到前端吗?
微服务是构建可以独立工作的小型自治团队的流行方式。不幸的是,就其本质而言,微服务只在后端工作。即使有最好的微服务架构,前端开发仍然需要高度的相互依赖,这会引入耦合和通信开销,这会拖慢每个人的速度。
我们可以采用微服务架构模式并将其应用到前端吗?事实证明我们可以。Netflix、Zalando和Capital One等公司已将这一模式推向前沿,为微前端奠定了基础。本文将探讨微前端、它们的优点和缺点,以及它们与传统微服务的不同之处。
前端的微服务
当我们将微服务方法带到前端时,我们就会得到微前端。换句话说,微前端由不同团队拥有的组件组成,这些组件可以独立部署。组装这些组件以创建一致的用户体验。
从左到右,我们展示了 4 个场景。 跨越所有场景,中间的水平线将前端与后端分开。
在左侧,我们有一个跨越前端和后端(包括 UI)的单体。 接下来,我们在后端有一个单体应用程序,在前端有一个 SPA 网页(UI 与后端分离)。 在第三个位置,我们在前端有相同的 SPA,但在后端将单体拆分为微服务。 最后一个场景在后端使用微服务,在前端使用微前端。 前端包括与其余部分隔离的不同组件或小部件。
单体可以以不同的方式分解。我们可以拆分前端和后端或在后端使用微服务。我们甚至可以将前端重新创建为由不同团队管理的隔离组件的集合。
使用微前端,没有一个团队拥有整个 UI。相反,每个团队都拥有一块屏幕、页面或内容。例如,一个团队可能负责搜索框,而另一个团队可能会根据用户的口味对建议进行编码。其他团队可能会对音乐播放器进行编码、管理播放列表或呈现计费页面。我们增加了复杂性,但作为回报,团队获得了更多的自主权。
音乐流媒体网站的线框图。 总共有5个团队:“主页”本身的一个,播放列表的一个,推荐的一个,搜索框的一个,音乐播放器的一个。 每个团队都在主页内分配了相应的小部件或组件。
前端功能由不同的团队管理,独立部署,并以透明的方式注入到最终用户的主页中。
微前端的好处和挑战
微前端提供与微服务类似的好处。也就是说,我们可以通过将前端代码分解成独立的部分来扩大开发规模,由不同的团队负责。与微服务一样,每个功能都可以随时自行发布,几乎不需要协调。这导致更频繁的更新。
垂直团队
微前端可以创建垂直团队,这意味着一个全栈开发团队可以同时拥有后端和前端的功能。
垂直团队处理给定功能或组件的所有功能和代码。 我们有 3 个团队:建议、搜索和播放列表。 每个团队都独立于其他团队管理其微服务后端和微前端。
全栈垂直团队负责功能或组件的前端和后端。
可连续部署的组件
微前端的每个部分都是一个可部署的单元。这允许团队发布他们的更改,而无需等待发布火车或依赖其他团队完成他们的工作。最终结果是前端可以每天更新几次。
每个团队都有一个单独的源存储库、CI/CD 管道和生产服务,为微前端内容提供服务。
所有独立微前端的组合在客户端呈现一个前端。
每个团队都可以拥有单独的存储库、CI/CD 管道和服务机器。或者,我们可以在 monorepo 上托管所有内容,并拥有一个共享的 CI/CD 管道。
微前端设计的挑战
微前端的主要挑战是创建一个快速响应的客户端。我们绝不能忽视这样一个事实,即前端存在于内存、CPU 和网络有限的环境中,否则我们就有可能导致 UI 缓慢。
简洁的用户界面对于产品的成功至关重要。最近的一项调查指出,“1 秒内加载的网站的转化率是 5 秒内加载的网站的 3 倍”。用户必须等待的每一秒,钱都会被扔出窗外。
除了微服务所面临的所有挑战之外,微前端设计还带来了一些问题:
- 隔离:每个团队的代码最终都必须在同一个浏览器上共存。我们必须慎重隔离单独的模块以避免代码或样式冲突。
- 共享资源:为避免重复并保持前端精简,组件应尽可能共享资产和库,这可能会产生不良耦合。
- 可访问性:严重依赖 JavaScript 来呈现页面会对可访问性产生负面影响。
- 样式:当 UI 由各个团队制作的组件组成时,保持一致的外观更加复杂。小的风格不一致会让人感到不和谐。
- 协调:有这么多活动部件,API 需要非常明确和稳定。团队必须协调微前端中不同组件之间以及后端微服务之间的通信方式。
构建微前端的原则
有两种互补的方法可以从单独的微前端组件渲染统一的 UI:服务器端渲染和客户端渲染。
服务器端渲染 (SSR)
服务器端渲染提供更快的性能和更易于访问的内容。在服务器上渲染是快速提供内容的好选择——尤其是在低功耗设备(如低端手机)上。当 JavaScript 被禁用时,它也是一种合适的后备模式。
服务器端渲染示意图。 网络服务器轮询各种微服务。 他们用网络服务器组装并转发给用户浏览器的 HTML 片段进行回复。
网络服务器从不同微服务提供的内容中组装完整的页面。这是第一个内容丰富的页面。然后可以使用水合来添加更多动态内容。
我们有几种执行 SSR 的方法:
- 服务器端包含(SSI):是一种由网络服务器执行的简单脚本语言。该语言使用指令将 HTML 片段构建成一个完整的页面。这些片段可能来自其他文件或程序的响应。所有主要的网络服务器都支持 SSI,包括 Apache、Nginx 和 IIS。
- iframes:古老的 iframe 功能允许我们在页面上嵌入任意 HTML 内容。
- Edge Side Includes (ESI):一种更现代的 SSI 替代方案。ESI 可以处理变量,有条件,并支持更好的错误处理。缓存 HTTP 服务器(例如Varnish )支持 ESI 。
因此,例如,我们可以使用 SSI 从 HTML 渲染页面:
该virtual关键字使网络服务器从 URL 或 CGI 程序请求内容。在我们的例子中,我们需要设置网络服务器以/hello-world使用合适的片段根据路径响应请求:
SSR 在许多 Web 框架中用于渲染第一个屏幕。此外,还有一些有趣的特定于 SSR 的实用程序,例如compoxure、nodei和Tail。
客户端渲染 (CSR)
客户端渲染通过从微服务获取数据并操作 DOM 在用户浏览器中构建页面。大多数 Web 框架使用某种形式的 CSR 来改善用户体验。
客户端渲染示意图。 用户的浏览器从 CDN 下载页面源。
在加载时,页面从不同的微服务端点加载数据并动态呈现视图。
CSR 使用端点提供的数据在用户浏览器上动态呈现页面。
我们编写松散耦合组件的主要工具是自定义元素。自定义元素是 HTML 标准的一部分。它们允许我们创建新的 HTML 标记并将逻辑和行为附加到它们。
使用 JavaScript 从页面动态装载和卸载自定义元素:
定义后,我们可以像使用任何其他 HTML 标记一样使用新元素:
在示例中,整个页面将包含一个用于获取 JavaScript 组件的脚本标记:
虽然大多数前端框架都可用于微前端,但有些框架是专门为它们设计的:
- Piral:实现称为pilets的独立组件。Pilets 是捆绑内容和行为的模块。
- Ragu:框架的框架。它允许我们将编写在任何框架中的代码作为小部件嵌入。
- 单一 SPA:一个元框架,用于将 UI 拼凑在一起,使用 React、Angular 和 Ember 等前端框架的任意组合。
- Frint:另一个用于构建基于组件的应用程序的模块化框架。与 React、Vue 和 Preact 集成。
- Module Federation:一个 WebPack 插件,通过捆绑单独的构建来创建单页应用程序 (SPA)。这些构建可以独立于每个构建
结论
切换到微前端架构可以给我们的开发团队更多的自主权,从而加速开发。但是,适用于微服务的相同警告也适用于微前端。我们需要经过验证的设计,这意味着微前端不适合新建项目。
新项目最好采用传统模式,例如由单个团队管理的单页应用程序 (SPA)。只有前端经受住了时间的考验,我们才能将微前端视为前进的方向。