2020年4月,有贊美業的前端團隊歷經7個月時間,完成了美業PC架構從單體SPA到微前端架構的設計、遷移工作。PPT在去年6月份就有了,現在再整理一下形成文章分享給大家。
頭圖
目錄
-
Part 01 “大話”微前端
-
微前端是什么
-
背景
-
目標
-
達成價值
-
缺點
-
-
Part 02 架構與工程
-
微前端方案有哪些
-
架構設計選型注意點
-
需求分析
-
設計原則
-
應用架構圖
-
系統拆分
-
時序圖
-
前端流程圖
-
-
Part 03 關鍵技術
-
關鍵技術一覽
-
架構核心
-
注冊中心
-
代碼復用
-
子應用
-
-
Part 04 項目實施
-
立項前的心路
-
參考微前端資料
-
進行PC架構優化計劃
-
風險
-
迭代立項
-
進展
-
后續計劃
-
Part 01 “大話”微前端
把這個事情的前因后果講清楚
微前端是什么
想要回答這個問題直接給一個定義其實沒那么難,但是沒接觸過的同學未必理解。所以需要先介紹一下背景,再解釋會更容易明白。
web發展1
這張圖,展示了軟件開發前端后分工的三個時期:
-
單體應用:在軟件開發初期和一些小型的Web網站架構中,前端后端數據庫人員存在同一個團隊,大家的代碼資產也在同一個物理空間,隨著項目的發展,我們的代碼資產發展到一定程度就被變成了巨石。
-
前后端分離:前端和后端團隊拆分,在軟件架構上也有了分離,彼此依靠約定去協作,大家的生產資料開始有了物理上的隔離。
-
微服務化:后端團隊按照實際業務進行了垂直領域的拆分單一后端系統的復雜度被得到分治,后端服務之間依靠遠程調用去交互。這個時候前端需要去調用后端服務時候,就需要加入一層API網關或者BFF來進行接入。
web發展2
現在很多互聯網公司的研發團隊的工作模式更靠近這種,把整個產品拆分成多個阿米巴模式的業務小組。
在這種研發流程和組織模式下,后端的架構已經通過微服務化形成了拆分可調整的形態,前端如果還處于單體應用模式,不談其它,前端的架構已經給協作帶來瓶頸。
另外 Web 3.0 時代來臨,前端應用越來越重,隨著業務的發展迭代和項目代碼的堆積,前端應用在勤勞的生產下演變成了一個龐然大物。人關注復雜度的能力有限,維度大概維持在5~8左右。單體應用聚合的生產資料太多,帶來復雜性的維度太多,也容易引發更多的問題。簡而言之,傳統的SPA已經沒辦法很好的應對快速業務發展給技術底層的考驗。
我們的產品和前端項目也同樣遇到了這個問題。如何解決這個問題呢?
其實后端的發展已經給出了可借鑒的方案,在理念上參照微服務/微內核的微前端架構應時而生。
想要解決這個問題,在吸引力法則的指引下我們遇到了微前端架構,也驗證了它的確幫助我們解決了這個難題。
現在給出我們的微前端這樣一種定義:
微前端是一種類似于微內核的架構,它將微服務的理念應用于瀏覽器端,即將 Web 應用由單體應用轉變為多個小型前端應用聚合為一的應用。多個前端應用還可以獨立運行、獨立開發、獨立部署。
背景
背景
-
美業PC作為一個單體應用經歷4年迭代開發,代碼量和依賴龐大,純業務代碼經統計有60多萬行
-
工程方面,構建部署的速度極慢,開發人員本地調試體驗差效率低,一次簡單的構建+發布需要7+8=15分鐘以上
-
代碼方面,業務代碼耦合嚴重,影響范圍難以收斂,多次帶來了“蝴蝶效應”式的的線上Bug和故障
-
技術方面,通用依賴升級帶來的改動和回歸成本巨大,涉及例如Zent組件、中臺組件等依賴包相關的日常需求和技術升級幾乎不可推動
-
測試方面,單應用應對多人和多項目發布,單應用發布總和高且非常頻繁,每次的集成測試都有沖突處理和新問題暴露的風險
-
組織方面,單應用也無法很好應對業務小組的開發組織形式,邊界職責不清晰且模塊開發易干擾
-
架構方面,前端無法和后端形成對應的領域應用開發模式,不利于業務的下沉,也無法支持前端能力的服務化和對技術棧的演進依賴
總體來說,臃腫的單體應用模式,給開發人員帶來了無法忍受的難處,給快速支撐業務帶來了很大的瓶頸,也沒有信心應對接下來的業務的繼續拓展。對美業PC進行架構調整就是非常迫切和有價值的事情了
目標
-
業務架構層面,圍繞美業PC的業務形態、項目架構以及發展趨勢,將大型多團隊協同開發的前端應用視為多個獨立團隊所產出功能的組合。
-
技術架構層面,解耦大型前端應用,拆分成基座應用、微前端內核、注冊中心、若干獨立開發部署的子系統,形成分布式體系的中心化治理系統。
-
軟件工程方面,保證漸進式遷移和改造,保證新老應用的正常運行。
達成價值
業務價值
-
實現了前端為維度的產品的原子化,如果整合新業務,子應用可以快速被其他業務集成
-
以業務領域劃分,讓組織架構調整下的項目多人協作更職責清晰和成本低,且適應組織架構調整
-
減慢系統的熵增,鋪平業務發展道路。
工程價值
-
實現了業務子應用獨立開發和部署,構建部署的等待耗時從15分鐘降到了1分半
-
支持漸進式架構,系統子應用之間依賴無關,可以單個升級依賴,技術棧允許不一致,技術迭代的空間更大
-
前端能力能夠服務化輸出
-
架構靈活,新的業務可以在不增加現存業務開發人員認知負擔的前提下,自由生長無限拓展
缺點
一個架構的設計其實對整體的一個權衡和取舍,除了價值和優勢之外,也帶來一些需要去考慮的影響。
缺點
Part 02 架構與工程
從全局視角把握成果
微前端方案有哪些
-
使用 HTTP 服務器反向代理到多個應用
-
在不同的框架之上設計通訊、加載機制
-
通過組合多個獨立應用、組件來構建一個單體應用
-
使用 iFrame 及自定義消息傳遞機制
-
使用純 Web Components 構建應用
-
結合 Web Components 構建
微前端
每種方案都有自己的優劣,我們兄弟團隊采用了最原始的網關轉發配置類似 Nginx 配置反向代理,從接入層的角度來將系統組合,但是每一次新增和調整都需要在運維層面去配置。
而 iframe 嵌套是最簡單和最快速的方案,但是 iframe的弊端也是無法避免的。
Web Components的方案則需要大量的改造成本。
組合式應用路由分發方案改造成本中等且滿足大部分需求,也不影響個前端子應用的體驗,是當時比較先進的一種方案。
架構設計選型注意點
-
如何降低系統的復雜度?
-
如何保障系統的可維護性?
-
如何保障系統的可拓展性?
-
如何保障系統的可用性?
-
如何保障系統的性能?
綜合評估之后我們選用了組合式應用路由分發方案,但是仍然有架構整體藍圖和工程實現需要去設計。
需求分析
-
子應用獨立運行/部署
-
中心控制加載(服務發現/服務注冊)
-
子應用公用部分復用
-
規范子應用的接入
-
基座應用路由和容器管理
-
建立配套基礎設施
設計原則
-
支持漸進式遷移,平滑過渡
-
拆分原則統一,嘗試領域劃分來解耦
應用架構圖
應用架構圖
系統拆分
系統拆分
這里拆分需要說明三個點:
-
獨立部署(服務注冊):上傳應用資源包(打包生成文件)到Apollo配置平臺,是一個點睛之筆
-
服務化和npm包插件化的區別是不需要通過父應用構建來集成,彼此依賴無關,發布獨立,更加靈活/可靠
-
同時 Apollo 承載了注冊中心的功能,可以省去子應用的web服務器的這一層,簡化了架構
時序圖
時序圖
前端流程圖
流程圖
## Part 03 關鍵技術
落地中有哪些值得一提的技術細節
關鍵技術一覽
我們按項目拆分來結構化講述,有架構核心、注冊中心、子應用、代碼復用四篇。
其中包含了這些技術點:
-
Apollo
-
Apollo Cli
-
Version Manage
-
Sandbox
-
RouterMonitor
-
MicroPageLoader
-
Shared Menu
-
Shared Common
[架構核心]消息通信
消息通信
消息通信1
消息通信2
消息通信3
[架構核心]路由分發
路由分發
當瀏覽器的路徑變化后,最先接受到這個變化的是基座的router,全部的路由變化由基座路由 RouterMonitor 掌管,因為它會去劫持所有引起url變化的操作,從而獲取路由切換的時機。如果是apps/xxx/#
之前的變化,只會攔截阻止瀏覽器再次發起網頁請求不會下發,沒有涉及#之前的url變化就下發到子應用,讓子應用路由接管。
[架構核心]應用隔離
主要分為 JavaScript執行環境隔離 和 CSS樣式隔離。
JavaScript 執行環境隔離:每當子應用的JavaScript被加載并運行時,它的核心實際上是對全局對象 window 的修改以及一些全局事件的的改變,例如 JQuery 這個js運行之后,會在 window 上掛載一個 window.$ 對象,對于其他庫 React、Vue 也不例外。為此,需要在加載和卸載每個子應用的同時,盡可能消除這種沖突和影響,最普遍的做法是采用沙箱機制 SandBox。
沙箱機制的核心是讓局部的 JavaScript 運行時,對外部對象的訪問和修改處在可控的范圍內,即無論內部怎么運行,都不會影響外部的對象。通常在 Node.js 端可以采用 vm 模塊,而對于瀏覽器,則需要結合 with 關鍵字和 window.Proxy 對象來實現瀏覽器端的沙箱。
CSS 樣式隔離:當基座應用、子應用同屏渲染時,就可能會有一些樣式相互污染,如果要徹底隔離 CSS 污染,可以采用 CSS Module 或者命名空間的方式,給每個子應用模塊以特定前綴,即可保證不會相互干擾,可以采用 webpack 的 postcss 插件,在打包時添加特定的前綴。
對于子應用與子應用之間的CSS隔離就非常簡單,在每次應用加載是,就將改應用所有的 link 和 style 內容進行標記。在應用卸載后,同步卸載頁面上對應的 link 和 style 即可。
[架構核心]核心流程圖
我們把路由分發、應用隔離、應用加載、通用業務邏輯收納到到了微前端內核的二方包中,用作各個業務線復用,在內部達成統一約定。
內核流程圖
[注冊中心]Apollo
其實大部分公司在落地微前端方案的時候,并有沒所謂的注冊中心的概念。為什么我們的微前端也會有注冊中心這個概念和實際存在呢?選型的思考點也主要來自我們后端的微服務架構。
注冊中心
為什么選擇引入注冊中心增加整體架構的復雜度?
兩個原因:
-
我們的子應用之間雖然不需要通信,但是也存在基座應用需要所有子應用的資源信息的情況,用來維護路由對應子應用資源地址的映射。大部分公司落地時候,都把子應用的地址信息硬編碼到了基座。這樣子應用增刪改時候,就需要去重新部署基座應用,這違背了我們解耦的初衷。注冊中心把這份映射文件從基座剝離出來了,讓架構具備了更好的解耦和柔性。
-
要知道我們的子應用的產物入口是 hash 化的上傳到 CDN 的 JS 文件,同時避免子應用發布也需要發布基座應用。有兩個解決方案,一種是增加子應用的 Web 服務器,可以通過固定的 HTTP 服務地址拿到最新的靜態資源文件。一種就是增加注冊中心,子應用發布就是推送新的 JS地址給到 注冊中心,子應用的架構就可以更薄。
需要一個注冊中心的話,我們也有兩種方案,一種是自己自研一個專門服務于自己的微前端,雖然可以更加貼合和聚焦,但是作為注冊中心,高可用的技術底層要求下的熔斷降級等機制必不可少,這些研發難度大成本也高。還有一種是直接應用成熟的提供注冊中心能力的開源項目或者依賴公司的已經存在的技術設施組件。
最后我們確定在選用公司內部的基礎技術設施的 Apollo 項目,優勢有這么兩方面。
-
項目本身開源,成熟程度很高,在多環境、即時性、版本管理、灰度發布、權限管理、開放API、支持端、簡單部署等功能性方面做得很不錯,是一個值得信賴的高可用的配置中心。
-
公司內部針對做了私有化定制和部署,更加適配業務,并且在 Java 和 Node 場景下都有穩定和使用,有維護人員值班。
apollo-basic
子應用的打包構建體驗
-
定位:一個子應用構建完是一個帶 hash 的靜態資源,等待被基座加載。
-
怎么做:
-
打包一個單入口的靜態資源,同時暴露全局方法給基座
-
每次構建生成帶 hash 的入口 app.js
-
獲取打包產出生成上傳配置
-
根據環境參數上傳到apollo
-
-
體驗如何
非常輕量,無須發布,構建即可
子應用如何推送打包完成的 cdn 地址給 Apollo
apollo頁面
-
獲取打包完成的產物的 JSON,獲取入口文件 Hash,和當前項目的基礎信息。
-
基于上述配置生成內容,然后調用 Apollo 平臺開放的 API 上傳到 Apollo。
如何進行多環境發布及服務鏈協作
微應用發布
-
環境主要分為測試、預發、生產。
-
打包完成后,根據微前端構建平臺指定環境。
-
推送配置時候,指定 Apollo 對應的環境集群就好了。
-
基座應用在運行時候,會根據環境與 Apollo 交互對應環境集群的注冊表信息。
[代碼復用]子應用之間如何復用公共庫
1、添加 shared 為遠程倉庫
git?remote?add?shared?http://gitlab.xxx-inc.com/xxx/xxx-pc-shared.git
2、將 shared 添加到 report 項目中
git?subtree?add?--prefix=src/shared?shared?master
3、拉取 shared 代碼
git?subtree?pull?--prefix=src/shared?shared?master
4、提交本地改動到 shared
git?subtree?push?--prefix=src/shared?shared?hotfix/xxx
注:如果是新創建子應用 1-2-3-4 ;如果是去修改一個子引用 1-3-4
[代碼復用]使用shared需要注意什么
-
修改了 shared 的組件,需要 push 改動到 shared 倉庫
-
如果一個 shared 中的組件被某個子應用頻繁更新,可以考慮將這個組件從 shared 中移除,內化到子應用中
[子應用]子應用如何接入
首先,我們需要明白我們對子應用的定位:
一個子應用構建完后是一個帶 hash 的靜態資源,等待被基座加載,然后在中心渲染視圖,同時擁有自己的子路由
第一步,根據我們的模板新建一個倉庫,并置入對應子應用的代碼
子應用目錄結構
第二步,接入shared以及修改一系列配置文件
第三步,進行開發所需要的轉發配置
第四部,運行,并嘗試打包部署
[子應用]子應用能獨立調式嗎?怎么基座應用聯調?
-
開啟基座,端口和資源映射到本地再調式
-
Zan-proxy
-
本地 Nginx 轉發
[子應用]子應用開發體驗
開發體驗
Part 04 項目實施
一個問題從出現到被解決走過的曲折道路
1.立項前的心路
-
看過微前端這個概念,覺得花里胡哨,玩弄名詞,強行造出新概念。
-
對項目的目前出現的問題有個大概感知(是個問題)
-
從業務出發利用現有知識背景思考解決手段(幾乎無解)
-
回想了解過微前端架構的概念和場景,感受到兩者有契合(人生若只如初見)
-
參考行業的解決方案印證,決定用微前端來脫掉膨脹的包袱(原來是該拆了)
-
首先把項目在前端架構優化理了一遍,輸出架構圖(項目整體上探路)
-
接下來梳理各個業務模塊的依賴,看下有哪些(子應用分析)
-
大量和不同人的聊天、了解、討論,獲取支撐技術選型的信息(外界專家)
-
確定微前端架構在美業下的落地基本模型(架構基本)
-
進行概要技術設計(具象化)
-
明確迭代范圍
-
技術評審
-
拉幫結伙/分工
-
kickoff
-
然而故事才剛剛開始…
2.參考微前端資料
微前端資料
3.進行PC架構優化計劃
PC架構優化計劃1
PC架構優化計劃2
PC架構圖
4.風險
預知
-
開發人員投入度不足
-
技術上的不確定性來更多工期風險
-
細節的技術實現需要打磨耗時超出預期
-
部分功能難以實現
意外
-
對項目架構理解不準確
-
任務拆分和邊界理解不到位
-
測試人員投入不足
-
協作摩擦
5.迭代立項
kickoff
6.進展
-
PC微前端基座應用已上線
-
PC數據拆分成子應用已上線
-
協調中臺前端抽取了美業微前端內核
-
通用工具方法和枚舉的可視化
-
搭配Apollo平臺形成了前端子應用資源的注冊中心
-
子應用接入文檔輸出
-
若干前端技術體系的優化
7.后續計劃
afterplan
關于本文
作者:邊城到此莫若
https://segmentfault.com/a/1190000040106401