微前端的起源

微前端的概念最早由 thoughtworks 在 2016 年提出。其核心思路是借鉴后端微服务架构理念,将一个单体的庞大的前端应用拆分为多个简单独立的前端工程。每个前端工程可以独立开发、测试、部署。最终再由一个容器应用,将拆分后的微前端工程组合为一个整体,面向用户提供服务。

微前端架构图

 

微前端的架构方式所带来的好处也是显而易见的:

  • 降低代码耦合
  • 微前端可独立部署
  • 团队可以按照业务垂直拆分更高效

常见的实现方式

微前端的概念最近一两年很火。但必须指出的是,微前端并不是一门新技术,而是一种新的架构方式。社区开发者们充分发挥聪明才智,提供了不少实现思路。

iframe

对于前端同学来讲,最容易想到的就是 iframe 了。iframe 天然具备微前端的基因。我们只需将单体的前端应用,按照业务模块进行拆分,分别部署。最后通过 iframe 进行动态加载即可。 一个简单的实现如下:

<html>
  <head>
    <title>微前端-ifame</title>
  </head>
  <body>
    <h1>我是容器</h1>
    <iframe id="mfeLoader"></iframe>
    <script type="text/javascript">
      const routes = {
        '/': 'https://app.com/index.html',
        '/app1': 'https://app1.com/index.html',
        '/app2': 'https://app2.com/index.html',
      };

      const iframe = document.querySelector('#mfeLoader');
      iframe.src = routes[window.location.pathname];
    </script>
  </body>
</html>
复制代码

优点

  • 实现简单
  • 天然具备隔离性

缺点

  • 主页面和 iframe 共享最大允许的 HTTP 链接数。
  • iframe 阻塞主页面加载。
  • 浏览器的后退按钮无效

服务端模板组合

有年代感的前端程序员对这种方式一定不陌生。常见的实现方式是,服务端根据路由动态渲染特定页面的模板文件。架构图如下:

 

服务端模板组合

 

容器模板代码如下:

<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>微前端-服务端模板</title>
  </head>
  <body>
    <h1>容器应用</h1>
    <!--# include file="$PAGE.html" -->
  </body>
</html>
复制代码

通过 Nginx 服务器根据 url 路径动态设置要加载的模板:

server {
    listen 8080;
    server_name localhost;

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

    rewrite ^/$ http://localhost:8080/app redirect;

    location /app {
      set $PAGE 'app';
    }
    location /app1 {
      set $PAGE 'app1';
    }
    location /app2 {
      set $PAGE 'app2';
    }

    error_page 404 /index.html;
}
复制代码

优点

  • 实现简单
  • 技术栈独立

缺点

  • 需要额外配置 Nginx
  • 前后端分离不彻底

微前端框架 single-spa

对于时刻将 “没有 js 做不到的事情” 视为座右铭的前端程序员们是不可能不造轮子的,鼎鼎大名的 single-spa 就这么被造出来了。

 

single-spa

 

借助 single-spa,开发者可以为不同的子应用使用不同的技术栈,比如子应用 A 使用 vue 开发,子应用 B 使用 react 开发,完全没有历史债务。

single-spa 的实现原理并不难,从架构上来讲可以分为两部分:子应用和容器应用。

子应用与传统的单页应用的区别在于

  • 不需要 HTML 入口文件,
  • js 入口文件导出的模块,必须包括 bootstrap、mount 和 unmount 三个方法。

容器应用主要负责注册应用,当 url 命中子应用的路由时激活并挂载子应用,或者当子应用不处于激活状态时,将子应用从页面中移除卸载。其核心方法有两个:

  • registerApplication 注册并下载子应用
  • start 启动处于激活状态的子应用。

以下是 single-spa 的简单示例:

容器应用代码

<html>
<body>
    <script src="single-spa-config.js"></script>
</body>
</html>
复制代码

single-spa-config.js 代码如下:

import * as singleSpa from 'single-spa';
const appName = 'app1';
const app1Url = 'http://app1.com/app1.js'

singleSpa.registerApplication('app1',() => loadJS(app1Url), location => location.pathname.startsWith('/app1'))

singleSpa.start();
复制代码

loadJS 方法是伪代码,表示加载 app1.js。开发者需要自己实现,或者借助 systemJS 来实现。

子应用代码:

//app1.js
let domEl;
export function bootstrap(props) {
    return Promise
        .resolve()
        .then(() => {
            domEl = document.createElement('div');
            domEl.id = 'app1';
            document.body.appendChild(domEl);
        });
}
export function mount(props) {
    return Promise
        .resolve()
        .then(() => {
            domEl.textContent = 'App 1 is mounted!'
        });
}
export function unmount(props) {
    return Promise
        .resolve()
        .then(() => {
            domEl.textContent = '';
        })
}
复制代码

优点

  • 纯前端解决方案
  • 可以使用多种技术栈
  • 完善的生态

缺点

  • 上手成本高
  • 需要改造现有应用
  • 跨应用的联调变得复杂

适用场景

以上介绍了三种常见的微前端架构方式。天生喜爱新事物的前端同学,早就想要一试了。那么问题来了。哪些场景适合微前端架构?采用哪种微前端实现方式?我的看法很简单:

  1. 业务模块相对独立的复杂单体应用
  2. 综合考虑团队技术能力和业务现状选择适合的方式

总结

以上介绍了三种常见的微前端实现方式:

  • 使用 iframe 组合
  • 服务端模板渲染组合
  • 微前端框架 single-spa

 

posted @ 2020-11-23 13:19  simple-love  阅读(469)  评论(0编辑  收藏  举报