1. 程式人生 > 實用技巧 >微前端的起源

微前端的起源

微前端的概念最早由 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,開發者可以為不同的子應用使用不同的技術棧,比如子應用 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