后端有 微服务,那前端呢?初探 微前端 的世界

本文首发于微信公众号:大迁世界, 我的微信:qq449245884,我会第一时间和你分享前端行业趋势,学习途径等等。
更多开源作品请看 GitHub https://github.com/qq449245884/xiaozhi ,包含一线大厂面试完整考点、资料以及我的系列文章。

前言

最近笔者在工作上一直听到后端工程师们在谈论 Microservices(微服务) 的架构设计,听到的当下立马去查询才知道原来 Microservices 这麽潮,身为前端工程师的我当然也希望前端也可以有这麽新颖的架构,于是这篇文章就要来跟读者介绍 Micro Frontends(微前端)。

什麽是 Microservices?

在开始进入本篇文章主题之前要先跟读者们介绍什麽是 Microservices。

Microservices 是一种软件架构,专注开发在每一个小型功能或者服务上,最后再利用模组化的方式组合出一个大型的应用程式。

如果读者是前端工程师的话可能会觉得上面的叙述很像是 ES6 module 的架构,开发者只需要专注在每个 module 上的开发,最后再利用 Bundler 打包这些 module 形成一个完整的页面甚至是应用程序,像下图这样。

后端有 微服务,那前端呢?初探 微前端 的世界_第1张图片

不过后端跟前端完全不一样,后端是藉由一个又一个的 requestreal time 的执行相关的代码,所以在 Microservices 的架构中,想要让一个又一个的服务能互相沟通,这时候就是要仰赖各个 API 了。

后端有 微服务,那前端呢?初探 微前端 的世界_第2张图片

但这时候会有一个很大的问题,假如这三个 Service 对于前端来讲有高度相依性,以上图为例:一个完整的购物网站必须要先让使用者登入后才可以进行购买商品以及去购物车结帐,这时候在 Client 端就必须要分别打三次 API 并且互相等待才可以完成这整个流程,甚至假如刚好不小心有一个 Service 坏了需要重新启动,这时候可能会先产生一个过渡期的 API 避免 Client 端打到有问题的 Service,可是 Client 端也不可能每次都会去记住这个新产生的 API,所以这势必会造成一个很大问题。

这时候其实可以在这些 Services 上添加一个 API Gateway,对于 Client 端来说我只需要对到一个 Gateway 就好,对于这个 Gateway 来说我一样是去呼叫各个 Service 并且把资源都处理完后再回传给前端。

后端有 微服务,那前端呢?初探 微前端 的世界_第3张图片

如果有读者本身是 SRE 熟悉 Docker 或者 K8s 这种用来自动部属容器化应用程式的平台,对于上面这张图应该更熟悉了!像 K8s 就有类似 API Gateway 的 Ingress 而 Docker 则有 routing mesh。

不过光有 API Gateway 其实还没办法凸显 Microservices 的特色,在 Microservices 的架构中其实每个 Service 都可以有自己的 DB,目的就是为了不要让每个 Service 之间会互相关联。

后端有 微服务,那前端呢?初探 微前端 的世界_第4张图片

但这样做其实有几个缺点

很难保证数据的一致性

以上图为例,假如今天有一个 member 被注销帐号,但这个 member 在被注销帐号之前在 shopping cart 这个 Service 中有待结帐商品,这时候就会出现 member 数据不一致的问题。

DB 数据遗失

当某一个 Service 坏了需要重启,这时候 DB 的数据有可能就会遗失导致后续的数据出现错误。

为了改善这些缺点这时候就可以将这些 Service 的 DB 设计成可弃性,换句话说就是这些 DB 只是用来作为短期的数据存取而已,背后还有一个共用的大数据库去更新这些数据,通常都会利用 Redis 这种 cache DB 来进行设计。

为什麽需要 Microservices?

Microservices 的好处就是可以专注开发在每个小服务上,举例来说以一个购票网站可能会在短时间内涌入大量的流量,这时候 race condition 就显得相当重要,这时候就可以利用 Go 语言进行开发,亦或者是要开发一个以效能为主的服务,这时候就可以用 Rust 进行开发。

上面也提到 Microservices 必须要仰赖 API 之间的沟通,所以通常在企业等级的产品上都会有拆分模组贩售的需求,假如这时候就是利用 Microservices 架构进行开发的话就很好拆分每个服务了。

后端有 微服务,那前端呢?初探 微前端 的世界_第5张图片

这些都是使用 Microservices 后所能带来的好处,所以假如读者今天需要开发一个非常複查且庞大的平台,这时候不妨可以利用 Microservices 的架构进行开发喔!

Monolithic Architecture(单体式架构)

讲完了 Microservices 后相信读者应该对于这种架构有了初步的认识,与 Microservices 不一样的架构就是 Monolithic Architecture(单体式架构)。

一般来说我们正常开发都是使用 Monolithic Architecture,在 Monolithic Architecture 的架构中都会把平台中所有内容都包装起来,像下图这样:

后端有 微服务,那前端呢?初探 微前端 的世界_第6张图片

这种架构不是不好,但假如今天想要拆分或者扩充平台上的 Service 其实都会比较麻烦一些,而且也会怕牵一发而动全身,甚至所有的 Service 背后都会连到同一个 DB,这样极有可能会让 DB connection 同时连线过高导致 request 一直发送失败,所以这也是 Microservices 想要解决的其中一个痛点。

什麽是 Micro Frontends(微前端)?

Micro Frontends 可以想像成前端版的 Microservices,在后端的世界中强调一个又一个 Service 而在前端的世界中则是强调一个又一个的 modules,如何将网页中每一个 module 有效的拆分就是 Micro Frontends 要做的工作。

Micro Frontends 的实现方式

接下来要跟读者介绍的是 Micro Frontends 的实现方式,其实 Micro Frontends 有蛮多种实现方式的:

iframe

透过 iframe 的特性让每个被载入的区块页面都可以独立运行,假如需要有数据上的沟通也可以利用 window.postMessage 来完成,但这样做会有非常多的缺点,像是有可能载入同样的代码、UI 难以控制、甚至可能会有潜在的安全风险,所以笔者还是建议读者不要用此方式来进行 Micro Frontends 的实现方式。

Client side 利用 JavaScript 载入 module

这个方法简单又粗暴,就是利用 JavaScript create 出 script tag 后,接著再用 script tag 去载入相关的 module,最后再将其内容塞进去对应的 div 内,但缺点就是无法使用 SSR,整体写法会像下图这样。

后端有 微服务,那前端呢?初探 微前端 的世界_第7张图片

Web Components

Web Components 可以说是最多人拿来讨论的 Micro Frontends 的实现方式了,虽然我们在现今的网页架构中可以自由地 import 大佬们写好的组件,但难免都会遇到以下几个问题:

  1. 组相依性的问题:需要安装只有该组件才会使用的 library,这会造成整个 node_modules 相当庞大。
  2. Scope 问题:前端为了样式上的变化通常都会有藉由许多的 className 来进行样式上的改动,但有可能因为该组件也有撰写一样的 className 导致很多时候都需要各种 override,长期下来也会是一种项目维护上的负担。
  3. 版本相容性问题:只要框架进行大改版,基本上就很容易出现组件无法兼容新版本的状况,这时候只能组件作者升级版本之后才能再次使用,相信这个状况也是许多开发者都会面临到的困境。

因此 Web Components 的出现就是希望可以解决上述的问题,而 Web Components 一共由以下三种元素组成:

Custom elements

自订一个语意化标签来引用 components,并且利用 JavaScript 来建立 custom elements、shadow DOM、HTML templates 三者之间的关联,这种自定义标签会像这样:

Shadow DOM

Shadow DOM 简单来说就是在现有的 DOM tree 中产生出一个完全独立于其他元素的 sub DOM tree,藉由此方法就可以让 sub DOM tree 自己独立运作并且不会干扰到其他 DOM tree 上的元素。

HTML templates

利用