微前端的起源
微前端的概念最早由 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 = '';
})
}
複製程式碼
優點
- 純前端解決方案
- 可以使用多種技術棧
- 完善的生態
缺點
- 上手成本高
- 需要改造現有應用
- 跨應用的聯調變得複雜
適用場景
以上介紹了三種常見的微前端架構方式。天生喜愛新事物的前端同學,早就想要一試了。那麼問題來了。哪些場景適合微前端架構?採用哪種微前端實現方式?我的看法很簡單:
- 業務模組相對獨立的複雜單體應用
- 綜合考慮團隊技術能力和業務現狀選擇適合的方式
總結
以上介紹了三種常見的微前端實現方式:
- 使用 iframe 組合
- 服務端模板渲染組合
- 微前端框架 single-spa