其他分享
首页 > 其他分享> > 转发:走进微前端架构的世界

转发:走进微前端架构的世界

作者:互联网

前端开发在程序猿中无疑是一个比较苦逼的存在,作为一个前端开发,你必须要掌握Javascript,HTML,CSS这三大基础。Javascript作为网络时代最为重要的开发语言,由于其设计上的限制,一直在演进,经历了ES3,ES5,ES6(ECMAScript 2015)... ... 而简单的CSS也无法完成你复杂的需求,你需要Less/Sass/Sytlus来增强你的CSS的功能。这些还远远只是一小部分,你还需要了解:

最可怕的是,这些东西都在飞快地发展和变化中,当你正忙于学习ES8,ES9,ES10的新特性的时候,今天我要和大家分享的希望不是压死前端开发小骆驼的最后一根稻草--微前端。

目录:

1.什么是微前端

2.为什么需要微前端,它有什么优势

3.如何实现微前端架构

4.运行时微前端的具体实现方式

5.微前端的问题和缺点

一、什么是微前端

而提到微前端就离不开微服务,大家对微服务都比较熟悉了,微服务允许后端体系结构通过松散耦合的代码库进行扩展,每个代码库负责自己的业务逻辑,并公开一个API,每个API均可独立部署,并且各自由不同的团队拥有和维护。

前端架构经历了从单体,到前后端分离,再到微服务,最终发展到现在的微前端的过程如下图所示:

微前端的思路是把微服务的架构引入到前端,其核心都是要能够以业务为单元构建端到端的垂直架构,使得单个的团队能够独立自主的进行相关的开发,同时又具备相当的灵活性,按需求来组成交付应用。

“微前端”一词最早于2016年底在ThoughtWorks 技术雷达中提出的。它将微服务的概念扩展到了前端世界。当前的趋势是构建一个功能强大且功能强大的浏览器应用程序(也称为单页应用程序),该应用程序位于微服务架构之上。随着时间的流逝,通常由一个单独的团队开发的前端层会不断增长,并且变得更加难以维护。

微前端背后的想法是将网站或Web应用程序视为由独立团队拥有的功能的组合。每个团队都有自己关心和专长的不同业务或任务领域。一个团队是跨职能的,并且从数据库到用户界面,端到端地开发其功能。

但是,这个想法并不新鲜。它与“单体系统”概念有很多共同点。在过去,类似的方法被称为“垂直系统的前端集成”。但是微前端显然是一个更友好,更轻巧的术语。

在微服务的架构中,后台的服务已经按照业务进行了分离,而前端仍然是一个单体构建,通过网关来调用不同的后台服务。这个微服务的思路是相违背的,这也就造成了你的后端团队是按照业务分割的,但是前端团队仍然是一个整体。微前端可以有效地改进这一点。

微前端的核心思路其实是远程应用程序,包含组件/模块/包的运行时加载

如上图,对于用户而言,访问的是一个微前端的容器(container),容器加载运行在远程服务上的应用,把这些远程应用作为组件/模块/包在本地浏览器中加载。

二、为什么需要微前端?

它有什么优势?

在前面我们看到的微前端之前的架构,所有的前端还是一个单体,前端团队会依赖所有的服务或者后台的API,前端开发会成为整个系统的瓶颈。使用微前端,就是要让前端业务从水平分层变为垂直应用的一部分,进入业务团队,剥离耦合。

那么微前端有什么好处,为什么要采用微前端架构呢?

因此,微前端和微服务的本质都是关于去耦合。而只有当应用程序达到一定规模时,这才开始变得更有意义。

三、如何实现微前端架构

微前端不是一个库,是一种前端架构的设计思路,要实现微前端,本质上就是在运行时远程加载应用。

实现微前端,有几个思路,从构建的角度来看有两种,编译时构建微前端和运行时构建微前端:

从前后端责任分层来看,可以从前端或者后端来实现。

通过客户端框架来实现

微前端通常由客户端工具来支持实现(听上去好有道理),有许多支持客户端开发微前端的实现工具,包括:Piral,Open Components,qiankun,Luigi,Frint.js等。其中qiankun是蚂蚁金服开发的。

在客户端还可以通过辅助库的方式来实现,辅助库可以为共享依赖项,路由事件或不同的微前端及其生命周期来提供一些基础架构。

下面的一个示例是通过诸如导入映射或打包特定块等机制处理共享依赖关系。

相关的工具有Webpack5 Module Federation,Siteless,Single SPA,Postal.js等

通过服务器端实现

微前端并非只能在客户端来实现,类似于服务端渲染,同样可以通过服务端来实现。

服务端微前端的支持工具有:Mosaic,PuzzleJs,Podium,Micromono等。

好了,说了这么多我相信你是一脸懵逼的,到底怎么实现的?我们抛开架构不说,来看看到底如何实现吧。

四、运行时微前端的具体实现方式

Iframe

iframes是可以在html中嵌入另一个HTML。下面就是用iframe实现微前端的一个例子:

<!DOCTYPE html>
<html>
<body>
<iframe src="http://localhost:3006" width="600" height="900">
  <p>Your browser does not support iframes.</p>
</iframe>
<iframe src="http://localhost:3007" width="600" height="900">
  <p>Your browser does not support iframes.</p>
</iframe>
</body>
</html>

如果不考虑体验问题,iframe 几乎是最完美的微前端解决方案了。iframe 提供了浏览器原生的隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。但它的最大问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。这里的主要问题包括:

所以虽然使用iframe可以实现远程加载的效果,但是因为这些限制,很少会有应用会使用。

Nginx路由

利用Ngix路由,我们可以把不同的请求路由到不同的微前端的应用。

例如Nginx的路由能力,在前端可以动态请求不同的后端应用,而每一个后端应用独立运行,前端可以把这些不同的后端应用加载,编排在一起。下面的代码是一个Nginx的配置,customers/users/admins分别表示了三个不同的应用,前端通过路由来加载位于不同服务的后端应用。

worker_processes 4;
events { worker_connections 1024; }
http {
    server {
        listen 80;
        root  /usr/share/nginx/html;
        include /etc/nginx/mime.types;
        location /app1 {
            try_files $uri app1/index.html;
        }

        location /app2 {
            try_files $uri app2/index.html;
        }

        location /app3 {
            try_files $uri app3/index.html;
        }
    }
}

无论你采用哪一种的微前端架构,Nginx方向代理或者其它的API网关的解决方案都能够提供方便灵活的后端路由功能。但是通过这种方式,需要定义一个通用可扩展的路由规则,否则当引入新的应用的时候,还需要修改Nginx的路由配置,那就很不方便了。

Webpack 5 Module Federation

Webpack5 的Module Federation是一个令人兴奋的革新,它能够很方便的支持微前端的构建。模块联合允许JavaScript应用程序从另一个应用程序动态加载代码,并在此过程中能共享依赖关系。如果使用Module Federation的应用程序不具有联合代码所需的依赖关系,则Webpack将从该联合构建源中下载缺少的依赖关系。

在Module Federation的上下文中,启动代码是一种将运行时代码附加到远程容器启动序列的实施策略。这真的很有用,因为通过Hook无法访问ModuleFederation及其运行时,无法对其进行扩展或添加一行代码,这些代码可以像动态设置远程容器的公共路径那样进行操作。这在普通的webpack应用程序中是微不足道的,但是在一个无法访问的自定义运行时容器中却很难做到,该容器为模块联合远程编排提供了动力。简单来说,Module Federation注入一段运行时的代码来负责加载和编排远程的应用代码,并能够管理和加载远程应用的依赖。

下面是一个对应的例子:

module.exports = {
    mode: 'development',
    devServer: {
        port: 8080,
    },
    plugins: [
        new ModuleFederationPlugin({
            name: 'container',
            remotes: {
                microFrontEnd1: 'microFrontEnd1@http://localhost:8081/remoteEntry.js',
                microFrontEnd2: 'microFrontEnd2@http://localhost:8082/remoteEntry.js',            },
        })
    ]
};

上面的代码是微前端的容器端的配置,容器负责加载其它远程应用的代码。这个例子里,它加载了两个远程应用。

module.exports = {
    mode: 'development',
    devServer: {
        port: 8081,
    },
    plugins: [
        new ModuleFederationPlugin({
            name: 'microFrontEnd1',
            filename: 'remoteEntry.js',
            exposes: {
                './MicroFrontEnd1Index': './src/index',
            },
        }),
    ]
};

每一个微前端的Webpack配置如上。

利用ModuleFederationPlugin,remote可以用来加载远端的应用,而Expose可以把自己的组件暴露为远端组件。

在container中,只需要调用以下的代码来加载远端组件。

import 'microFrontEnd1/MicroFrontEnd1Index';
import 'microFrontEnd2/MicroFrontEnd2Index';

Module Federation的加载过程如上图所示:

  1. localhost 加载index.html
  2. main.js 是Module Federation的核心的编排代码,负责加载远程组件。
  3. remoteEntry.js 是Module Federation暴露的远程组件的代码
  4. src_ 是打包后的代码,其中 bootstrap_js是容器侧的代码,index_js是微前端侧的代码。

Module Federation实现了类似动态链接库的能力,可以在运行时加载远程代码,远程代码本质上是一个加载在window上的全局变量,Module Federation可以帮助解决依赖的问题。Javascrip作为上古语言,没有提供依赖管理,导致留给各路大神各种发挥的空间。

Module Federation的缺点就是依赖Webpack 5,包直接挂载为全局变量。

EMP微前端是基于Module Federation的微前端解决方案。

Single SPA

单页面应用是当今为Web应用的主流,区别于传统的多页面应用,整个SPA只有一个页面,其内容都是通过Javascript的功能来加载。

SPA是一个Web应用程序,仅包含一个HTML页面。提供动态更新,它允许在不刷新页面的情况下与页面进行交互。利用单页应用程序,可以显着降低服务器负载并提高加载速度,从而获得更好的用户体验,因为SPA仅在先前加载整个页面时才按需导入数据。

除了开发复杂,对于SEO不友好,但页面应用的最大技术缺陷是URL不适合共享,因为SPA只有一个地址。

single-spa是一个框架,用于将前端应用程序中的多个JavaScript微前端组合在一起。

使用single-spa构建前端可以带来很多好处,例如:

single-spa应用程序包含以下内容:

Single-SPA注册的应用程序拥有普通SPA所具有的所有功能,只是它没有HTML页面。SPA包含许多已注册的应用程序,每个应用程序都有其自己的框架。已注册的应用程序具有其自己的客户端路由和它们自己的框架/库。它们呈现自己的HTML,并且在安装时有完全的自由去做他们想做的任何事情。挂载的概念是指已注册的应用程序是否正在将内容放在DOM上。决定是否挂载已注册应用程序的是其活动功能。每当未挂载已注册的应用程序时,它都应保持完全休眠状态直到挂载。

Single SPA的样例代码如下:

1. 微前端代码:

import React from "react";
import ReactDOM from "react-dom";
import singleSpaReact from "single-spa-react";
import Root from "./root.component";
const lifecycles = singleSpaReact({
  React,
  ReactDOM,
  rootComponent: Root,
  errorBoundary(err, info, props) {
    // Customize the root error boundary for your microfrontend here.
    return null;
  },
});
export const { bootstrap, mount, unmount } = lifecycles;

Single SPA的微前端是纯的JS组件,不包含HTML,需要通过容器来加载。

2.容器的Root Config

在容器侧,需要通过Import Map或者Webpack来定义远程组件,并注册远程应用。

{
  "imports": {
    "@naughty/root-config": "//localhost:9000/naughty-root-config.js",
    "@naughty/app": "//localhost:8500/naughty-app.js",
    "react": "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js",
    "react-dom": "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js"
  }
}

容器侧的HTML文件使用import map来定义远程依赖,其中root-config是编排代码,负责远程应用的注册和加载。同时需要定义所有共享的依赖,这里例子中是react和react-dom

import { registerApplication, start } from "single-spa";
registerApplication({
  name: "@single-spa/welcome",
  app: () =>
    System.import(
      "https://unpkg.com/single-spa-welcome/dist/single-spa-welcome.js"
    ),
  activeWhen: ["/welcome"],
});
registerApplication(
  '@naughty/app',
  () => System.import('@naughty/app'),
  location => location.pathname.startsWith('/app')
);
start({
  urlRerouteOnly: true,
});

在root-config中,我们注册了两个远程应用,使用不同的url来加载。/welcome会加载welcome应用,而/app会加载我们的app应用。

Single SPA的核心是利用不同的URL路由来加载远程组件,它可以和Webpack(打包时构建依赖)或者Import Map(运行时使用浏览器导入依赖)一起工作。注意,不要在你的微前端中混用两种依赖机制。

Single SPA还提供一个layout 引擎,可以帮助你快速的构建微前端。

相比Module Federation,Single SPA的代码和生命周期的管理更清楚,提供清晰的接口,缺点是共享的依赖需要手工通过import map来管理。

要做一个好的微前端因为受限于浏览器和JS的一些特性,并不容易。除了我们今天分享的内容,还面临着诸多的挑战:如何解决css/js的冲突,使得组件和应用完全隔离;如何解决不同应用间的通信;如何处理路由;如何保证UI风格的统一等等。

五、微前端的问题和缺点

讲了这么多的优点和实现,那么微前端是不是解决前端开发问题的银弹呢?当然不是。所有的架构都是取舍和权衡,这个世界上并不存在银弹,微前端架构和微服务一样也存在他的弊端,单体架构未必就差。

1. 微前端的构建通常比较复杂,从工具,打包,到部署,微前端都是更为复杂的存在,天下没有免费的午餐,对于小型项目,它的成本太高。

2. 每个团队可以使用不同的框架,这个听上去很美,但是实际操作起来,除了要支持历史遗留的应用,它的意义不大。同时也为带来体验上的问题。可以远程加载不同的框架代码是一回事,把它们都用好是另一回事。

3. 性能上来看,如果优化得不好微前端的性能可能会存在问题,至少微前端框架是额外的一层加载。如果不同的微前端使用了不同的框架,那么每一个框架都需要额外的加载。

微前端架构还在发展之中,本文提到的iframe/nginx/module federation/single-spa只是诸多解决方案中的一小部分,前端的发展变化和生态系统实在是丰富,其他的方案诸如umd/乾坤,Piral,open comonent等等。当使用你也可以选择标准的Web Component或者ES Modules来构建微前端,但是这些标准的浏览器支持不是特别好,这个是前端开发永远的痛。(诅咒IE)

大家对于微前端有什么想法或者问题,欢迎一起讨论。

标签:架构,前端,应用程序,应用,转发,SPA,代码,加载
来源: https://www.cnblogs.com/mate-ui/p/15750537.html