实施微前端的六种方式

来源:http://www.chinese-glasses.com 作者:Web前端 人气:94 发布时间:2020-04-15
摘要:答案是肯定的。微前端模式通常采用的方法是将微前端的一个片段(可能实现为微前端Web 组件)与微服务配对以提供其 UI。 后端:函数调用 - 远程调用 反向代理除了将微应用请求路由

答案是肯定的。微前端模式通常采用的方法是将微前端的一个片段(可能实现为微前端 Web 组件)与微服务配对以提供其 UI。

后端:函数调用 - 远程调用

反向代理除了将微应用请求路由到合适的服务器外,还会将宏应用请求路由到宏应用服务器上。这个宏应用服务器以特殊方式为组合应用提供 HTML。它会通过代理服务器的 URL 从浏览器收到对 index.html 的请求,然后获取 index.html,对其进行简单但重要的转换后返回它。

使用 iFrame 创建容器

微前端架构:细节决定成败

从这种定义上来看,它可能算不上并不是一种微前端——它可以满足了微前端的三个要素,即:独立运行独立开发独立部署。但是,配合上前端框架的组件 Lazyload 功能——即在需要的时候,才加载对应的业务组件或应用,它看上去就是一个微前端应用。

本文介绍的微前端解决方案的关键要素,就是设置一个专用服务器来处理微应用组合。这个服务器会代理对托管各个微应用的服务器发出的请求。当然,我们需要花些功夫来设置和管理这个服务器。一些微前端方法(例如 single-spa)为了简化部署和配置工作,会尽量精简这种特殊服务器的设置任务。

其次,采用这种方式还有一个限制,那就是:规范!***规范!*规范!。在采用这种方案时,我们需要:

微前端是一种新模式,其中 Web 应用程序 UI(前端)由一些半独立的片段组成,可以由不同的团队使用不同的技术来构建。微前端架构类似一种后端架构,其中后端由一些半独立的微服务组成。

如果这一类应用过于复杂,那么它必然是要进行微服务化的拆分。因此,在采用 iframe 的时候,我们需要做这么两件事:

本文将提供一份微前端教程,重点在于具体的实现,主要介绍微前端架构中的重要问题及其可能的解决方案。

现有的 Web 框架已经有一些可以支持 Web Components 的形式,诸如 Angular 支持的 createCustomElement,就可以实现一个 Web Components 形式的组件:

微前端架构为微前端框架的结构元素提供了方法。它还定义了它们之间的关系,控制 UI 片段的组装和通信方式,以实现最佳的开发人员和用户体验。

同样的 Stencil 仍然也只是支持最近的一些浏览器,比如:Chrome、Safari、Firefox、Edge 和 IE11

一个微服务是体系结构中的一个元素,在该体系结构中,应用程序被构造为一些互操作服务的集合。如果前端采用微前端模式,则一个微服务可以与一个微前端配对。

在页面合适的地方引入或者创建 DOM用户操作时,加载对应的应用(触发应用的启动),并能卸载应用。

从正面的角度来说,iframe 有自己独立的 Content-Security-Policy(内容安全政策,CSP)。另外,如果 iframe 指向的微应用使用了 Service Worker 或实现了服务端渲染,那么一切都会按照预期正常工作。我们还可以为 iframe 指定各种沙箱选项以限制其能力,例如禁止导航到顶部框架等。

常见的方式有:

html head script src="/yumcha.js"/script /head body h1Hello, micro-frontend app./h1 !-- HERE ARE THE MICROAPPS! -- yumcha-portal name="microapp1" src=""/yumcha-portal yumcha-portal name="microapp2" src=""/yumcha-portal /body/html

如下是一个 Spring 框架,用于返回首页的示例:

如果(a)事实证明微前端是一种更好的架构方法,并且(b)我们可以弄清楚如何在实现它们的过程中满足当今 Web 领域的众多实际需求,微前端就可以在 Web 应用的生态系统中打下基础。

Web Components 离现在的我们太远,可是结合 Web Components 来构建前端应用,则更是一种面向未来演进的架构。或者说在未来的时候,我们可以开始采用这种方式来构建我们的应用。好在,已经有框架在打造这种可能性。

Yumcha 服务器

如下是一个在 React 中引用 Stencil 生成的 Web Components 的例子:

实现自定义元素

在几年前的一个项目里,我们当时正在进行遗留系统重写。我们制定了一个迁移计划:

render() { return html`iframe src=${this.src}/iframe`; }

通讯机制10bet,。直接在每个应用中创建postMessage事件并监听,并不是一个友好的事情。其本身对于应用的侵入性太强,因此通过iframeEl.contentWindow去获取 iFrame 元素的 Window 对象是一个更简化的做法。随后,就需要定义一套通讯规范:事件名采用什么格式、什么时候开始监听事件等等。

不管是哪种方式,只有在激活了微应用后 iframe 才会真正渲染,其代码才会被加载和执行。就我们使用 LitElement 的实现而言,假设激活状态由一个 activated 的实例变量表示,我们将得到以下内容:

iframe 可以创建一个全新的独立的宿主环境,这意味着我们的前端应用之间可以相互独立运行。采用 iframe 有几个重要的前提:

微前端架构是一种设计方法,其中,前端应用被分解为多个松散而协同工作的半独立“微应用”。微前端的思想来源于微服务,其名称也遵循了微服务的命名方式。那么,微前端的优势和好处在哪?让我们一起通过这篇微前端教程来了解。

在一个单体前端、单体后端应用中,有一个典型的特征,即路由是由框架来分发的,框架将路由指定到对应的组件或者内部服务中。微服务在这个过程中做的事情是,将调用由函数调用变成了远程调用,诸如远程 HTTP 调用。而微前端呢,也是类似的,它是将应用内的组件调用变成了更细粒度的应用间组件调用,即原先我们只是将路由分发到应用的组件执行,现在则需要根据路由来找到对应的应用,再由应用分发到对应的组件上。

render() { if (!this.activated) return html`{this.placeholder}`; else return html` iframe srcdoc="${this.content}" @load="${this.markLoaded}"/iframe`;}

复合型,对就是上面的几个类别中,随便挑几种组合到一起。我就不废话了~~。

一些浏览器已经支持,或正在计划为 iframe 支持 loading=lazy 属性,该属性会推迟加载非首屏的 iframe,直到用户将页面滚动到它们附近才开始加载,但这并未提供对延迟加载的细粒度控制,这种控制才是我们想要的。

首先,使用静态网站生成动态生成首页其次,使用 React 计划栈重构详情页最后,替换搜索结果页

微前端教程第一步:为组合应用作标记

因此,这种方式看起来更像是一个软件工程问题。

回到客户端,在我们实现微应用的过程中,要做到高效、延迟加载和无垃圾的渲染还有一个重要的层面需要关注。我们可以使用 src 属性(发出另一个网络请求),或使用 srcdoc 属性(由服务器为我们填充内容)来为每个微应用生成 iframe 标记。但是在这两种情况下,这个 iframe 中的代码都会立即启动,包括加载其所有脚本和链接标签、引导程序、任何初始 API 调用以及相关的数据处理——即使这个微应用根本没有被用户访问过。

有兴趣的读者,可以看看笔者之前写的微前端框架:Mooa。

这套设置的优点包括:首先,在对组合页面的 index.html 发出请求时,服务器可以从各个微应用服务器中整体检索各个页面,包括 SSR 渲染的内容(如果存在) ——并向浏览器提供一个完整的页面(包括可用于填充 iframe 的内容),而无需额外的服务器往返(使用不多的 srcdoc 属性)。代理服务器还可以完全掩盖微应用来源的细节。最后,由于所有应用请求都来自同一来源,因此它简化了 CORS 问题。

link rel="import" href="components/di-li.html"link rel="import" href="components/d-header.html"

微服务可以有 UI 吗?

注意:这里的前端应用指的是前后端分离的单应用页面,在这基础才谈论微前端才有意义。

如果不使用 iframe 的话,上面的某些问题也可以避免或减轻,我们稍后会讨论这种替代方法。

但是,不论怎样,与 iFrame 相比,其在技术上更具有可吹牛逼性,更有看点。同样的,与 iframe 类似,我们仍然面对着一系列的不大不小的问题:

尽管微前端最近在业内引发了很多关注,但到目前为止并没有出现一种绝对主流的实现方式,也没有公认“最佳”的微前端框架。实际上,根据目标和需求的不同,可行的方法有很多。要了解其中一些比较知名的实现,请参见附注。

在 Web Components 中集成现有框架

大家对 iframe 这个 HTML 元素又爱又恨,但实际上它们提供了极其有用的、坚如磐石的沙箱行为。但在使用 iframe 时还有很多问题需要注意,这些问题可能会对我们应用的行为和功能产生影响:

而后端在这个过程中,多了一个服务发现的服务,来管理不同微服务的关系。

解决方案是将每个微应用的状态编码为单个复合 URL,并使用小型的宏应用路由,该路由知道如何将这些复合 URL 组合在一起或者拆分开来。遗憾的是,这要求每个微应用都有特定于 Yumcha 的逻辑:从宏应用路由接收消息并更新微应用的状态,反之则是通知宏应用路由这个状态的变化,以更新复合 URL。例如,特定于 Angular 的可能是 YumchaLocationStrategy,或特定于 React 的元素。

复合型

时间: 2019-09-30阅读: 97标签: 微前端

它主要由四项技术组件:

我们利用 template 标签的怪癖来解决这个问题,在这个标签中包装了从宏应用服务器获取到的宏应用内容。事实上,当现代浏览器遇到 template 标记时,尽管它们没有“执行”它,但还是会 " 解析 " 它,这样就移除了诸如html、head和body标记之类的无效内容,同时保留其内部内容。因此head中的script和link标记以及body的内容都会留下来,这正是我们想要的,这样就可以将微应用内容正常插入页面了。

在未来,将有更多的框架可以使用类似这样的形式,集成到 Web Components 应用中。

iframe 的真正问题在于,它们的内容需要多个网络请求才能获取。首先接收顶级的 index.html,然后加载它的脚本,接下来解析它的 HTML——但是随后,浏览器必须为 iframe 的 HTML 发送另一个请求,等待接收它,解析并加载其脚本,然后渲染 iframe 的内容 。在许多情况下,iframe 的 JavaScript 也得单独加载,调用自己的 API;等到这些 API 调用返回,为视图准备的数据处理完毕之后才能显示出有意义的数据。

@RequestMapping("/name")public String name(){ String name = restTemplate.getForObject("", String.class); return Name" + name;}

首先我们来深入探讨,如何定义一款 " 宏应用 " 以及组成它的众多微应用。标记(Markup)一直是 Web 的核心要素,因此只需使用以下标记即可定义我们的宏应用:

那么,在微服务的情况下,它则会变成这样子:

微应用间通信

  • 列表 - 详情:

回到客户端,yumcha-portal标记被实例化并找到服务器放置在响应文档中的内容,并在适当的时间渲染 iframe,然后将内容分配给其 srcdoc 属性。如果我们不使用 iframe(请参见下文),则与yumcha-portal标签对应的内容(如果我们正在使用的话)会插入自定义元素的 shadow DOM 中,或者直接内联到文档中。

不同技术栈之间差异比较大,难以兼容、迁移、改造项目不想花费大量的时间在这个系统的改造上现有的系统在未来将会被取代系统功能已经很完善,基本不会有新需求

但是建立这种反向代理的成本会被我们从中获得的收益抵消。实际上,如果没有这种反向代理,我们基于微前端的应用就有一些重要的行为无法实现。有很多商业和免费的方案可以用来设置这种反向代理服务器。

HTML 内联框架元素iframe表示嵌套的正在浏览的上下文,能有效地将另一个 HTML 页面嵌入到当前页面中。

如上所述,在 iframe 中托管微应用确实存在一些缺点。有两种替代方案:直接将它们直接内嵌在页面的 HTML 中,或者将它们放置在 shadow DOM 中。两种选择都在某种程度上抵消了 iframe 的优缺点,但具体的方式有所不同。

路由分发式微前端,即通过路由将不同的业务分发到不同的、独立前端应用上。其通常可以通过 HTTP 服务器的反向代理来实现,又或者是应用框架自带的路由来解决。

我们将用于微应用的自定义元素命名为yumcha-portal,因为“portal”是 portal 提案中针对微应用推荐的术语,这一提案是为微前端定义标准 HTML 元素的早期尝试。

就当前而言,通过路由分发式的微前端架构应该是采用最多、最易采用的 “微前端” 方案。但是这种方式看上去更像是多个前端应用的聚合,即我们只是将这些不同的前端应用拼凑到一起,使他们看起来像是一个完整的整体。但是它们并不是,每次用户从 A 应用到 B 应用的时候,往往需要刷新一下页面。

如何定义微服务?

首页,用于面向用户展示特定的数据或页面。这些数据通常是有限个数的,并且是多种模型的。列表,即数据模型的聚合,其典型特点是某一类数据的集合,可以看到尽可能多的数据概要(如 Google 只返回 100 页),典型见 Google、淘宝、京东的搜索结果页。详情,展示一个数据的尽可能多的内容。

我们应该如何实现呢?因为它是自定义元素,所以当然是作为 Web 组件来实现了!我们有许多高水平的方案可供挑选,帮助我们编写和编译微前端 Web 组件。在本文中,我们将使用 Polymer 项目的最新版本 LitElement。LitElement 支持基于 TypeScript 的语法糖,为我们处理了大多数自定义元素样板。为了使yumcha-portal在我们的页面上可用,我们必须将相关代码作为script包括在页面内,之前的代码就是这样做的。

使用 Web Components 构建独立于框架的组件,随后在对应的框架中引入这些组件在 Web Components 中引入现有的框架,类似于 iframe 的形式

非 iframe 情况

而我们即又要拆分应用,又想 blabla……,我们还能怎么做?

这可能会导致我们不希望看到的延迟和渲染伪像,尤其在涉及多个微应用时更容易出现这类问题。如果 iframe 的应用实现了 SSR 还能好一些,但还是不能避免额外的往返行程。

如下是一个基于路由分发的 Nginx 配置示例:

基础知识什么是微前端?

在这种情况之下,我们就可以构建出独立于框架的组件。

什么是微前端架构?

就当前而言,有两种方式可以结合 Web Components 来构建微前端应用:

但是,使用诸如 Angular 和 React 之类的框架构建的应用可能不适合内联或 shadow DOM。对于这些情况,我们可能还是会使用 iframe。

@RequestMapping(value="/")public ModelAndView homePage(){ return new ModelAndView("/WEB-INF/jsp/index.jsp");}

但是yumcha-portal到底是做什么的呢?首先它可以使用指定的源创建一个 iframe:

因此在这种情况下,它适用于以下场景:

这套 Yumcha 宏应用 index.html 转换逻辑很容易用无服务器 lambda 函数的方式实现,也可以作为服务端框架(如 Express 或 Koa)的中间件来实现。

考虑到现有及常用的技术的局限性问题,让我们再次将目光放得长远一些。

尽管根据定义,构成宏应用的多个微应用是松散耦合的,但它们仍需要能够相互通信。例如,导航微应用需要发出通知,告知用户刚刚选择的某个微应用应被激活,并且要激活的应用需要能够接收此类通知。

不论是基于 Web Components 的 Angular,或者是 VirtualDOM 的 React 等,现有的前端框架都离不开基本的 HTML 元素 DOM。

路由

纯 Web Components 技术构建

基于存根的微应用控件

前者是一种组件式的方式,或者则像是在迁移未来的 “遗留系统” 到未来的架构上。

微前端架构可能更简洁,因此更易于推理和管理。多个独立的开发团队更容易协同开发单个前端应用。微前端模式可以让“新”应用与 " 旧 " 应用并行工作,从而提供了一种迁移手段。

这种方式看上去相当的理想,即能满足多个团队并行开发,又能构建出适合的交付物。

首先,iframe 在大小控制和自身布局方面有一些众所周知的怪癖。当然,CSS 会与 iframe完全隔离,这有利有弊。浏览器的“后退”按钮将正常工作,但是 iframe 的当前导航状态不会反映在页面的 URL 中,所以我们既不能通过剪切和粘贴 URL 来使微应用状态与宏应用保持一致,也无法对其进行深层链接。根据我们的 CORS 设置,从外部与 iframe 通信可能需要通过 postMessage 协议。必须处理跨 iframe 边界的身份验证事宜。一些屏幕阅读器可能会在 iframe 边界出错,或者需要 iframe 具有可以向用户告知的标题。

不管怎样,iframe 对于我们今年的 KPI 怕是带不来一丝的好处,那么我们就去造个轮子吧。

这只是 Yumcha 服务器众多有趣功能的冰山一角。比如说,我们想要添加一些功能来控制来自微应用服务器的 HTTP 错误响应的处理方式,或者控制响应速度非常慢的微应用的应对策略——如果一个微应用没有响应,我们可不想永远等下去!

统一依赖。统一这些依赖的版本,引入新的依赖时都需要一一加入。规范应用的组件及路由。避免不同的应用之间,因为这些组件名称发生冲突。构建复杂。在有些方案里,我们需要修改构建系统,有些方案里则需要复杂的架构脚本。共享通用代码。这显然是一个要经常面对的问题。制定代码规范。

在当今时代,我们所有人都希望 SPA 中的 URL 栏代表应用的视图状态,这样我们就可以剪切、粘贴、发送邮件、记录文本和链接到这个 URL,从而直接跳转到应用内的页面。但在微前端应用中,应用状态实际上是很多状态的组合,每个微应用对应一个状态。我们如何表示和控制这一切呢?

结论

关于第一个问题,没有人声称微前端是适用于所有用例的架构。特别是,单个团队没有什么理由采用微前端来开发新内容。下面这个问题留给读者思考:在哪些环境中,哪些类型的应用采用微前端模式比其他方法效果更好?

对于某个详情页面来说,它可能是这样的:

我们针对此问题的解决方案是,一开始将页面上的微应用表示为较小的未激活存根,等待将来激活。可以使用未充分利用的 Intersection-Observer API,通过微应用的视图区域来激活,或者更常见的是通过外部发送的预先通知来激活。当然,我们也可以指定立即激活微应用。

尽管Single-SPA已经拥有了大部分框架(如 React、Angular、Vue 等框架)的启动和卸载处理,但是它仍然不是适合于生产用途。当我基于 Single-SPA 为 Angular 框架设计一个微前端架构的应用时,我最后选择重写一个自己的框架,即Mooa。

这里,render 是标准的 LitElement 渲染 hook,使用它的 html 标记模板字面量。对于某些普通的场景来说,这种初级功能可能就够用了。

如果我们做的是一个应用平台,会在我们的系统中集成第三方系统,或者多个不同部门团队下的系统,显然这是一个不错的方案。一些典型的场景,如传统的 Desktop 应用迁移到 Web 应用:

具体来说,HTML 将为yumcha-portal标记解析,从 Node.js 生态系统中找一款合适的 HTML 解析器即可。使用yumcha-portal的 src 属性可以联系上运行微应用的服务器,并获取其 index.html——包括服务端渲染的内容(如果存在)。结果将作为script或template标记插入 HTML 响应中,这样就不会被浏览器执行。

import React from 'react';import ReactDOM from 'react-dom';import './index.css';import App from './App';import registerServiceWorker from './registerServiceWorker';import 'test-components/testcomponents';ReactDOM.render(App /, document.getElementById('root'));registerServiceWorker()

本文由10bet发布于Web前端,转载请注明出处:实施微前端的六种方式

关键词:

上一篇:JS中的空值

下一篇:没有了

频道精选

最火资讯