如何解構單體前端應用——前端應用的微服務式拆分
重新整理頁面?路由拆分?No,動態載入元件。
本文分為以下四部分:
- 前端微服務化思想介紹
- 微前端的設計理念
- 實戰微前端架構設計
- 基於 Mooa 進行前端微服務化
前端微服化
對於前端微服化來說,有這麼一些方案:
- Web Component 顯然可以一個很優秀的基礎架構。然而,我們並不可能去大量地複寫已有的應用。
- iFrame。你是說真的嗎?
- 另外一個微前端框架 Single-SPA,顯然是一個更好的方式。然而,它並非 Production Ready。
- 通過路由來切分應用,而這個跳轉會影響使用者體驗。
- 等等。
因此,當我們考慮前端微服務化的時候,我們希望:
- 獨立部署
- 獨立開發
- 技術無關
- 不影響使用者體驗
獨立開發
在過去的幾星期裡,我花費了大量的時間在學習 Single-SPA 的程式碼。但是,我發現它在開發和部署上真的太麻煩了,完全達不到獨立部署地標準。按 Single-SPA 的設計,我需要在入口檔案中聲名我的應用,然後才能去構建:
declareChildApplication('inferno', () => import('src/inferno/inferno.app.js'), pathPrefix('/inferno'));
同時,在我的應用裡,我還需要去指定我的生命週期。這就意味著,當我開發了一個新的應用時,必須更新兩份程式碼:主工程和應用。這時我們還極可能在同一個原始碼裡工作。
當出現多個團隊的時候,在同一份原始碼裡工作,顯然變得相當的不可靠——比如說,對方團隊使用的是 Tab,而我們使用的是 2 個空格,隔壁的老王用的是 4 個空格。
獨立部署
一個單體的前端應用最大的問題是,構建出來的 js、css 檔案相當的巨大。而微前端則意味著,這個檔案被獨立地拆分成多個檔案,它們便可以獨立去部署應用。
我們真的需要技術無關嗎?
等等,我們是否真的需要技術無關?如果我們不需要技術無關的話,微前端問題就很容易解決了。
事實上,對於大部分的公司和團隊來說,技術無關只是一個無關痛癢的話術。當一家公司的幾個創始人使用了 Java,那麼極有可能在未來的選型上繼續使用 Java。除非,一些額外的服務來使用 Python 來實現人工智慧。因此,在大部分的情況下,仍然是技術棧唯一。
對於前端專案來說,更是如此:一個部門裡基本上只會選用一個框架。
於是,我們選擇了 Angular。
不影響使用者體驗
使用路由跳轉來進行前端微服務化,是一種很簡單、高效的切分方式。然而,路由跳轉地過程中,會有一個白屏的過程。在這個過程中,跳轉前的應用和將要跳轉的應用,都失去了對頁面的控制權。如果這個應用出了問題,那麼使用者就會一臉懵逼。
理想的情況下,它應該可以被控制。
微前端的設計理念
設計理念一:中心化路由
網際網路本質是去中心化的嗎?不,DNS 決定了它不是。TAB,決定了它不是。
微服務從本質上來說,它應該是去中心化的。但是,它又不能是完全的去中心化。對於一個微服務來說,它需要一個服務註冊中心:
服務提供方要註冊通告服務地址,服務的呼叫方要能發現目標服務。
對於一個前端應用來說,這個東西就是路由。
從頁面上來說,只有我們在網頁上新增一個選單連結,使用者才能知道某個頁面是可以使用的。
而從程式碼上來說,那就是我們需要有一個地方來管理我們的應用:**發現存在哪些應用,哪個應用使用哪個路由。
管理好我們的路由,實際上就是管理好我們的應用。
設計理念二:標識化應用
在設計一個微前端框架的時候,為每個專案取一個名字的問題糾結了我很久——怎麼去規範化這個東西。直到,我再一次想到了康威定律:
系統設計(產品結構等同組織形式,每個設計系統的組織,其產生的設計等同於組織之間的溝通結構。
換句人話說,就是同一個組織下,不可能有兩個專案的名稱是一樣的。
所以,這個問題很簡單就解決了。
設計理念三:生命週期
Single-SPA 設計了一個基本的生命週期(雖然它沒有統一管理),它包含了五種狀態:
- load,決定載入哪個應用,並繫結生命週期
- bootstrap,獲取靜態資源
- mount,安裝應用,如建立 DOM 節點
- unload,刪除應用的生命週期
- unmount,解除安裝應用,如刪除 DOM 節點
於是,我在設計上基本上沿用了這個生命週期。顯然,諸如 load 之類對於我的設計是多餘的。
設計理念四:獨立部署與配置自動化
從某種意義上來說,整個每系統是圍繞著應用配置進行的。如果應用的配置能自動化,那麼整個系統就自動化。
當我們只開發一個新的元件,那麼我們只需要更新我們的元件,並更新配置即可。而這個配置本身也應該是能自動生成的。
實戰微前端架構設計
基於以上的前提,系統的工作流程如下所示:
整體的工程流程如下所示:
- 主工程在執行的時候,會去伺服器獲取最新的應用配置。
- 主工程在獲取到配置後,將一一建立應用,併為應用繫結生命週期。
- 當主工程監測到路由變化的時候,將尋找是否有對應的路由匹配到應用。
- 當匹配對對應應用時,則載入相應的應用。
故而,其對應的架構如下圖所示:
獨立部署與配置自動化
我們做的部署策略如下:我們的應用使用的配置檔案叫 apps.json
,由主工程去獲取這個配置。每次部署的時候,我們只需要將 apps.json
指向最新的配置檔案即可。配置的檔案類如下所示:
- 96a7907e5488b6bb.json
- 6ff3bfaaa2cd39ea.json
- dcd074685c97ab9b.json
一個應用的配置如下所示:
{
"name": "help",
"selector": "help-root",
"baseScriptUrl": "/assets/help",
"styles": [
"styles.bundle.css"
],
"prefix": "help",
"scripts": [
"inline.bundle.js",
"polyfills.bundle.js",
"main.bundle.js"
]
}
這裡的 selector
對應於應用所需要的 DOM 節點,prefix 則是用於 URL 路由上。這些都是自動從 index.html
檔案和 package.json
中獲取生成的。
應用間路由——事件
由於現在的應用變成了兩部分:主工程和應用部分。就會出現一個問題:只有一個工程能捕獲路由變化。當由主工程去改變應用的二級路由時,就無法有效地傳達到子應用。在這時,只能通過事件的方式去通知子應用,子應用也需要監測是否是當前應用的路由。
if (event.detail.app.name === appName) {
let urlPrefix = 'app'
if (urlPrefix) {
urlPrefix = `/${window.mooa.option.urlPrefix}/`
}
router.navigate([event.detail.url.replace(urlPrefix + appName, '')])
}
相似的,當我們需要從應用 A 跳轉到應用 B 時,我們也需要這樣的一個機制:
window.addEventListener('mooa.routing.navigate', function(event: CustomEvent) {
const opts = event.detail
if (opts) {
navigateAppByName(opts)
}
})
剩下的諸如 Loading 動畫也是類似的。
使用 Mooa 進行
So,我們就有了前端微服務框架 Mooa。它基於 single-spa && single-spa-angular-cli,並且符合以上的設計思想。
GayHub 地址:https://github.com/phodal/mooa
對於主工程而言,只需要以下的幾行程式碼就可以完成上面的功能:
``` http.get('/assets/apps.json') .subscribe(data => { data.map((config) => { that.mooa.registerApplication(config.name, config, mooaRouter.matchRoute(config.prefix)); }); this.mooa.start(); });
this.router.events.subscribe((event: any) => { if (event instanceof NavigationEnd) { that.mooa.reRouter(event); } }); ```
並新增一個對應的子應用路由:
{
path: 'app/:appName/:route',
component: HomeComponent
}
則如上所述的四個步驟。
對於子工程而言,則只需要一個對應的 Hook 操作:
mooaPlatform.mount('help').then((opts) => {
platformBrowserDynamic().bootstrapModule(AppModule).then((module) => {
opts['attachUnmount'](module);
});
});
並設定好對應的 base_href:
providers: [
{provide: APP_BASE_HREF, useValue: mooaPlatform.appBase()},
]
嗯,就是這麼簡單。DEMO 視訊如下:
Demo 地址見:http://mooa.phodal.com/
GitHub 示例:https://github.com/phodal/mooa
相關文章
- 微服務架構:拆分單體應用的難點微服務架構
- 微前端實戰:大型前端應用的拆分與治理前端
- 分散式應用服務的拆分分散式
- 前端單頁應用微服務化解決方案2 - Single-SPA前端微服務
- 如何把單體式應用拆解成微服務?【下】微服務
- 如何把單體式應用拆解成微服務?【上】微服務
- 前端插拔式 SPA 應用架構實現方案前端應用架構
- 用微前端的方式搭建類單頁應用前端
- 天天吹微服務,單體應用有啥不好?微服務
- charles前端應用前端
- 12-factor應用和微服務架構應用的區別微服務架構
- 單體應用、SOA、微服務,優劣勢都有哪些?微服務
- 前端:你要懂的單頁面應用和多頁面應用前端
- 如何從複雜單體應用快速遷移到微服務?微服務
- 前端單頁面應用的許可權管理前端
- 使用微服務構建現代應用程式微服務
- 前端之路:緊跟潮流,docker簡單應用前端Docker
- 前端構造桌面級應用(QQ音樂)前端
- 前端技術演進(四):前端三層結構與應用前端
- [翻譯]微服務設計模式 - 1. 單體應用模式微服務設計模式
- 如何 0 改造,讓單體/微服務應用成為 Serverless Application微服務ServerAPP
- 如何管理基於微服務的分散式應用程式微服務分散式
- 傳統應用系統架構向微服務應用架構升級的實戰案例微服務應用架構
- Java開發微服務實現分散式架構應用總結Java微服務分散式架構
- [譯]2020 年用各大前端框架構建的 RealWorld 應用對比前端框架架構
- 構建Spring Boot應用的微服務服務動態路由Spring Boot微服務路由
- 使用silky腳手架構建微服務應用架構微服務
- 分散式政企應用如何快速實現雲原生的微服務架構改造分散式微服務架構
- vue 單頁應用(spa)前端路由實現原理Vue前端路由
- 前端資料結構(1)之棧及其應用前端資料結構
- 微前端架構將應用拆分為多個小型模組,實現模組化的設計前端架構
- 如何在不重構的情況下將單體拆分成微服務?微服務
- 微前端概述(Micro Frontends) 以及相比單體應用,微前端能帶來什麼好處前端
- 三角函式在前端動畫中的應用函式前端動畫
- 微核心架構在大型前端系統中的應用架構前端
- 如何使用 docker 部署前端應用Docker前端
- 構建Spring Boot應用的微服務服務監控與告警Spring Boot微服務
- 單體應用 適合採用 dapr 構建嗎?