微前端-技术调研

2022-04-28 22:12:202022-05-12 22:57:34

微前端是近两年比较火热的话题,从国外的 single-spa 到阿里的 qiankun,从京东的 micro-app 到欢聚时代的 emp ,各家大厂使尽浑身解数推出自家的微前端框架,那么微前端到底是什么呢

什么是微前端

网上看到一句话对其总结的鞭辟入里

“微前端是一种架构风格,其中众多独立交付的前端应用组合成一个大型整体。”

微前端的概念最早在 2016 年底被提出微前端不是单纯的前端框架或者工具,而是一套架构体系,它将微服务的概念扩展到前端世界。在该技术架构下,新的/改造好之后的前端应用可由多个子应用构成,这些子应用间独立开发、独立部署、独立运行时、技术栈无关

微前端有如下特性:

  1. 技术栈无关

  2. 独立开发/部署

  3. 增量升级

既然微前端这么好,为什么直到 16 年才被提出来呢

其实早在上古时代,iframe就可称为微前端,为什么这么说呢,因为iframe可以做到完美隔离 javascript/css ,并且可适用 url 快速接入。那既然这么简单,为啥没有流行起来呢(在微前端领域)?因为iframe缺点同样明显:隔离过于简单粗暴,导致应用间数据交互困难;整个应用全量加载,性能较差;弹窗类业务无法正常施展。。。

为什么要做微前端

微前端既可将多个应用/项目整合到一起,又可减小各应用间的耦合性,提高项目的可扩展性

实现微前端有哪些方案

从集成方式出发可以大致分为构建时集成、运行时集成

构建时集成

最常见的是将子应用独立开发为一个单独的可发布的 Javascript 包,在主应用中引用并加载。但是,这种方法意味着我们必须重新编译并发布每个微前端,才能发布对产品任何单个部分的更改

运行时集成

和构建时集成相反,在运行时动态加载各应用

最简单的事通过 nginx 来分发

<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8" />
    <title>Feed me</title>
  </head>
  <body>
    <h1>Feed me</h1>
    <!--# include file="$PAGE.html" -->
  </body>
</html>
server {
    listen 8080;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;
    ssi on;

    # Redirect / to /browse
    rewrite ^/$ http://localhost:8080/browse redirect;

    # Decide which HTML fragment to insert based on the URL
    location /browse {
      set $PAGE 'browse';
    }
    location /order {
      set $PAGE 'order';
    }
    location /profile {
      set $PAGE 'profile'
    }

    # All locations should render through index.html
    error_page 404 /index.html;
}

这种方式也可勉强称为微前端,因为我们将每个业务都拆分到一个独立的应用中,可以交由独立的团队维护。除了这种服务端集成,在客户端也可以通过一定的手段拆分-集成

<html>
  <head>
    <title>Feed me!</title>
  </head>
  <body>
    <h1>Welcome to Feed me!</h1>

    <!-- These scripts don't render anything immediately -->
    <!-- Instead they attach entry-point functions to `window` -->
    <script src="https://browse.example.com/bundle.js"></script>
    <script src="https://order.example.com/bundle.js"></script>
    <script src="https://profile.example.com/bundle.js"></script>

    <div id="micro-frontend-root"></div>

    <script type="text/javascript">
      // These global functions are attached to window by the above scripts
      const microFrontendsByRoute = {
        "/": window.renderBrowseRestaurants,
        "/order-food": window.renderOrderFood,
        "/user-profile": window.renderUserProfile,
      };
      const renderFunction = microFrontendsByRoute[window.location.pathname];

      // Having determined the entry-point function, we now call it,
      // giving it the ID of the element where it should render itself
      renderFunction("micro-frontend-root");
    </script>
  </body>
</html>

在这里,我们将每个应用打包成一个 javascript 文件,在匹配到 URL 后执行对应的代码,渲染页面。

除了使用 javascript ,还可使用开篇提到的 iframe 集成,但 iframe 缺点过于明显就不讨论。此外,还可使用 web components 来实现,其也属于 javascript 因此不另列

<html>
  <head>
    <title>Feed me!</title>
  </head>
  <body>
    <h1>Welcome to Feed me!</h1>

    <!-- These scripts don't render anything immediately -->
    <!-- Instead they each define a custom element type -->
    <script src="https://browse.example.com/bundle.js"></script>
    <script src="https://order.example.com/bundle.js"></script>
    <script src="https://profile.example.com/bundle.js"></script>

    <div id="micro-frontend-root"></div>

    <script type="text/javascript">
      // These element types are defined by the above scripts
      const webComponentsByRoute = {
        "/": "micro-frontend-browse-restaurants",
        "/order-food": "micro-frontend-order-food",
        "/user-profile": "micro-frontend-user-profile",
      };
      const webComponentType = webComponentsByRoute[window.location.pathname];

      // Having determined the right web component custom element type,
      // we now create an instance of it and attach it to the document
      const root = document.getElementById("micro-frontend-root");
      const webComponent = document.createElement(webComponentType);
      root.appendChild(webComponent);
    </script>
  </body>
</html>

除了这两种分类,还可从拆分粒度来分类

基于路由分发

上面介绍的基本都是一个页面一个应用,那么还有没有粒度更细一点的:想在一个页面加载多个应用?🤔

前端微服务化

每个前端应用都是独立的、自主运行的,最后通过模块化的方式组合出完整的前端应用

比如借助上面的 Web Components 实现

<html>
  <head>
    <title>Feed me!</title>
  </head>
  <body>
    <h1>Welcome to Feed me!</h1>

    <!-- These scripts don't render anything immediately -->
    <!-- Instead they each define a custom element type -->
    <script src="https://browse.example.com/bundle.js"></script>
    <script src="https://order.example.com/bundle.js"></script>
    <script src="https://profile.example.com/bundle.js"></script>

    <div id="micro-frontend-root">
      <micro-frontend-browse-restaurants />
      <micro-frontend-order-food />
      <micro-frontend-user-profile />
    </div>
  </body>
</html>

在这里,我们加载的 js 资源可以是任何借助框架构建后生成的 web component 组件文件

总结

微前端是一种架构风格,其中众多独立交付的前端应用组合成一个大型整体。

微前端技术方案可分为运行时集成/构建时集成,或者按照拆分粒度划分为路由分发式和微服务化

参考

详解微前端