This article takes you into the world of micro front-end

Continue to create, accelerate growth! This is the 5th day of my participation in the "Nuggets Daily New Plan · June Update Challenge", click to view the details of the event

What is a micro frontend

Micro-Frontends is an architecture similar to microservices, which applies the concept of microservices to the browser side, that is, transforms a web application from a single monolithic application to an application that aggregates multiple small front-end applications into one . The term micro-frontends was mentioned in TECHNOLOGY RADAR in 2016.

The micro-frontend architecture has the following characteristics:

  • The technology stack is irrelevant. The main framework does not restrict the technology stack of the access application, and the micro-application has full autonomy

  • Independent development, independent deployment. The micro-application warehouse is independent, and the front and back ends can be developed independently. After the deployment is completed, the main framework automatically completes the synchronization update

  • Incremental upgrade. In the face of various complex scenarios, it is usually difficult for us to upgrade or refactor an existing system in full, and micro-frontends are a very good means and strategy for implementing incremental refactoring.

  • When running independently. State isolation between each micro-application, runtime state is not shared

What problems do micro frontends solve?

The micro front-end architecture is designed to solve the application of a single application in a relatively long time span, due to the increase and change of the participating personnel and teams, from an ordinary application to a frontend monolith application. unmaintainable problem. for example:

  • Originally a project managed by one team, managed by multiple teams later
  • As the size of the project becomes larger, the compilation speed becomes longer, and the R&D efficiency decreases.
  • The larger the project, the larger the complexity of the system, the lower the maintainability, and the higher the cost of reconstruction.
  • ...

After the micro-frontend is split into a container application and multiple sub-applications, each application can be deployed independently and isolated from each other, so as to achieve:

  • Improved R&D efficiency: parallel development of multiple business lines, team autonomy, and independent iteration
  • Operational risk de-escalation: change scope narrows
  • More technology choices: Each application can choose a technology stack that is more suitable for itself
  • Refactoring risk reduction: low-risk partial replacement, and complete large-scale refactoring gradually
  • ...

Micro front-end implementation scheme comparison

Nginx routing forwarding

通过Nginx配置反向代理来实现不同路径映射到不同应用,例如www.abc.com/app1对应app1,www.abc.com/app2对应app2,这种方案本身并不属于前端层面的改造,更多的是运维的配置

优点:

  • 简单,快速,易配置

缺点:

  • 在切换应用时会触发浏览器刷新,影响体验

iframe嵌套

父应用单独是一个页面,每个子应用嵌套一个iframe,父子通信可采用postMessage或者contentWindow方式

优点:

  • 实现简单,子应用之间自带沙箱,天然隔离,互不影响

缺点:

  • url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
  • UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
  • 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
  • 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。

Web Components

每个子应用需要采用纯Web Components技术编写组件,是一套全新的开发模式

优点:

  • 每个子应用拥有独立的script和css,也可单独部署

缺点:

  • 对于历史系统改造成本高,子应用通信较为复杂易踩坑

webpack5 的 Module Federation

使用 Module Federation,我们可以在一个应用中动态加载并执行另一个应用的代码,且与技术栈无关,并且能够共享模块,从而减小编译时间以及降低包体积

优点:

  • 能够共享模块,减小编译时间以及降低包体积

缺点:

  • 需要升级 webpack5,构建工具受限

组合式应用路由分发

每个子应用独立构建和部署,运行时由父应用来进行路由管理,应用加载,启动,卸载,以及通信机制

优点:

  • 纯前端改造,体验良好,可无感知切换,子应用相互隔离

缺点:

  • 需要设计和开发,由于父子应用处于同一页面运行,需要解决子应用的样式冲突,变量对象污染,通信机制等技术点

组合式应用路由分发是目前业内普遍使用的一种方案,并且能够满足大部分需求,接下来我们详细来看看这种实现方式

组合式应用路由分发的微前端实现思路

该方案使用的是基座模式,通过一个主应用(基座应用-Main APP),来管理其它应用(子应用-MicroAPP)。基座应用大多数是一个前端 SPA 项目,主要负责应用注册,路由映射,消息下发等,而微应用是独立前端项目,这些项目不限于采用 React,Vue,Angular 或者 JQuery 开发,每个微应用注册到基座应用中,由基座进行管理,但是如果脱离基座也是可以单独访问

当整个微前端框架运行之后,给用户的体验就是类似下图所示:

简单描述下就是基座应用中有一些菜单项,点击每个菜单项可以展示对应的微应用,这些应用的切换是纯前端无感知的

上面的实现过程主要如下:

  • 获取注册表和进行初始化,这些都是在基座应用中进行的
  • 路由分发。在浏览器路径发生变化后,基座应用会监听 hashchange 或者 popstate 事件,从而获取到路由切换的时机。通过查询注册信息可以获取到转发到那个微应用,经过一些逻辑处理后,采用修改hash方法或者pushState方法来路由信息推送给微应用的路由,微应用可以是手动监听hashchange或者popstate事件接收,或者采用React-router,vue-router接管路由,后面的逻辑就由微应用自己控制
  • 远程拉取资源,加载应用。这里一般有通过 JavaScript Entry 或者 HTML Entry 作为渲染入口
    • JavaScript Entry。通常是子应用将资源打成一个 entry script。但这个方案的限制也颇多,如要求子应用的所有资源打包到一个 js bundle 里,包括 css、图片等资源。除了打出来的包可能体积庞大之外的问题之外,资源的并行加载等特性也无法利用上

    注意:子应用也可以将包打成多个,然后利用 webpack 的 webpack-manifest-plugin 插件打包出 manifest.json 文件,生成一份资源清单,然后主应用的 loadApp 远程读取每个子应用的清单文件,依次加载文件里面的资源;不过该方案也没办法享受子应用的按需加载能力

    • HTML Entry。则更加灵活,直接将子应用打出来 HTML 作为入口,主框架可以通过 fetch html 的方式获取子应用的静态资源,同时将 HTML document 作为子节点塞到主框架的容器中。这样不仅可以极大的减少主应用的接入成本,子应用的开发方式及打包方式基本上也不需要调整,而且可以天然的解决子应用之间样式隔离的问题(后面提到)。这种方案可以通过 import-html-entry 完成

微前端的应用隔离

CSS 隔离

当主应用和微应用同屏渲染时,就可能会有一些样式会相互污染,可以采取以下两种方案

  • CSS Module
  • 命名空间,通过 webpack 的 postcss 插件,在打包时添加特定的前缀,即各个子应用使用特定的前缀来命名 class。但对于一些插入到 body 中的样式,比如 element UI 的 Popover 弹出框,这种就特殊处理

而对于微应用与微应用之间的CSS隔离就非常简单,在每次应用加载时,将该应用所有的link和style 内容进行标记。在应用卸载后,同步卸载页面上对应的link和style即可

JavaScript 隔离

每当微应用的 JavaScript 被加载并运行时,它的核心实际上是对全局对象 Window 的修改以及一些全局事件的改变,例如 jQuery 这个 js 运行后,会在 Window 上挂载一个 window.$ 对象,对于其他库 React,Vue 也不例外。为此,需要在加载和卸载每个微应用的同时,尽可能消除这种冲突和影响,最普遍的做法是采用沙箱机制(SandBox)

The core of the sandbox mechanism is to allow the local JavaScript runtime to control the access and modification of external objects, that is, no matter how the internal operation is performed, the external objects will not be affected. Usually, the vm module can be used on the Node.js side, while for the browser, the with keyword and the window.Proxy object need to be combined to implement the browser-side sandbox

Message Communication for Micro Frontends

Micro-frontends generally do not limit the framework an application uses. How to communicate between different applications and frameworks is a decision that needs to be carefully considered. There are many ways to communicate between applications. Of course, in order to communicate between multiple separate micro-applications, the intermediate media or global objects are essentially still inseparable.

custom event

Communicating through events should be the easiest and most versatile solution

// 监听事件
window.addEventListener('message', (event) => {
  // 处理事件
});

// 触发事件
window.dispatchEvent(new CustomEvent('message', { detail: input.value }))
复制代码

publish-subscribe

The communication mechanism of the message subscription (pub/sub) mode is very suitable. The event center Event will be defined in the base application, and each micro-application will register the event separately. When the event is triggered, the event center will distribute it uniformly. constitutes the basic communication mechanism

import { Observable } from 'windowed-observable';

const observable = new Observable('konoha');
observable.subscribe((ninja) => {
  console.log(ninja)
})

observable.publish('Uchiha Shisui');
复制代码

Web Workers

Event Communication via Web Workers

import Worky from 'worky'
const worker = new Worky("some-worker.js");

worker.on("eventName", function (some, data) {
  // 处理
});
worker.emit("someEvent", and, some, data);
复制代码

shared state

The main application creates a state store and shares it with sub-applications, which is suitable for scenarios where the main and sub-application technology stacks are the same.

Summarize

A micro frontend is an application that transforms a single giant application into multiple micro-applications and can solve the maintainability problem of "monolithic applications". There are many ways to implement micro-frontends, each of which needs to consider the issues of application isolation and application communication. At present, the most commonly used method is the combined route distribution method.

refer to

Guess you like

Origin juejin.im/post/7104253657251577886