有贊美業(yè)微前端的落地總結(jié)
大廠技術(shù)??高級前端??Node進階
點擊上方?程序員成長指北,關(guān)注公眾號
回復1,加入高級Node交流群
2020年4月,有贊美業(yè)的前端團隊歷經(jīng)7個月時間,完成了美業(yè)PC架構(gòu)從單體SPA到微前端架構(gòu)的設計、遷移工作。PPT在去年6月份就有了,現(xiàn)在再整理一下形成文章分享給大家。

目錄
Part 01 “大話”微前端
微前端是什么 背景 目標 達成價值 缺點 Part 02 架構(gòu)與工程
微前端方案有哪些 架構(gòu)設計選型注意點 需求分析 設計原則 應用架構(gòu)圖 系統(tǒng)拆分 時序圖 前端流程圖 Part 03 關(guān)鍵技術(shù)
關(guān)鍵技術(shù)一覽 架構(gòu)核心 注冊中心 代碼復用 子應用 Part 04 項目實施
立項前的心路 參考微前端資料 進行PC架構(gòu)優(yōu)化計劃 風險 迭代立項 進展 后續(xù)計劃
Part 01 “大話”微前端
把這個事情的前因后果講清楚
微前端是什么
想要回答這個問題直接給一個定義其實沒那么難,但是沒接觸過的同學未必理解。所以需要先介紹一下背景,再解釋會更容易明白。

這張圖,展示了軟件開發(fā)前端后分工的三個時期:
單體應用:在軟件開發(fā)初期和一些小型的Web網(wǎng)站架構(gòu)中,前端后端數(shù)據(jù)庫人員存在同一個團隊,大家的代碼資產(chǎn)也在同一個物理空間,隨著項目的發(fā)展,我們的代碼資產(chǎn)發(fā)展到一定程度就被變成了巨石。 前后端分離:前端和后端團隊拆分,在軟件架構(gòu)上也有了分離,彼此依靠約定去協(xié)作,大家的生產(chǎn)資料開始有了物理上的隔離。 微服務化:后端團隊按照實際業(yè)務進行了垂直領域的拆分單一后端系統(tǒng)的復雜度被得到分治,后端服務之間依靠遠程調(diào)用去交互。這個時候前端需要去調(diào)用后端服務時候,就需要加入一層API網(wǎng)關(guān)或者BFF來進行接入。

現(xiàn)在很多互聯(lián)網(wǎng)公司的研發(fā)團隊的工作模式更靠近這種,把整個產(chǎn)品拆分成多個阿米巴模式的業(yè)務小組。
在這種研發(fā)流程和組織模式下,后端的架構(gòu)已經(jīng)通過微服務化形成了拆分可調(diào)整的形態(tài),前端如果還處于單體應用模式,不談其它,前端的架構(gòu)已經(jīng)給協(xié)作帶來瓶頸。
另外 Web 3.0 時代來臨,前端應用越來越重,隨著業(yè)務的發(fā)展迭代和項目代碼的堆積,前端應用在勤勞的生產(chǎn)下演變成了一個龐然大物。人關(guān)注復雜度的能力有限,維度大概維持在5~8左右。單體應用聚合的生產(chǎn)資料太多,帶來復雜性的維度太多,也容易引發(fā)更多的問題。簡而言之,傳統(tǒng)的SPA已經(jīng)沒辦法很好的應對快速業(yè)務發(fā)展給技術(shù)底層的考驗。
我們的產(chǎn)品和前端項目也同樣遇到了這個問題。如何解決這個問題呢?
其實后端的發(fā)展已經(jīng)給出了可借鑒的方案,在理念上參照微服務/微內(nèi)核的微前端架構(gòu)應時而生。
想要解決這個問題,在吸引力法則的指引下我們遇到了微前端架構(gòu),也驗證了它的確幫助我們解決了這個難題。
現(xiàn)在給出我們的微前端這樣一種定義:
微前端是一種類似于微內(nèi)核的架構(gòu),它將微服務的理念應用于瀏覽器端,即將 Web 應用由單體應用轉(zhuǎn)變?yōu)槎鄠€小型前端應用聚合為一的應用。多個前端應用還可以獨立運行、獨立開發(fā)、獨立部署。
背景

美業(yè)PC作為一個單體應用經(jīng)歷4年迭代開發(fā),代碼量和依賴龐大,純業(yè)務代碼經(jīng)統(tǒng)計有60多萬行 工程方面,構(gòu)建部署的速度極慢,開發(fā)人員本地調(diào)試體驗差效率低,一次簡單的構(gòu)建+發(fā)布需要7+8=15分鐘以上 代碼方面,業(yè)務代碼耦合嚴重,影響范圍難以收斂,多次帶來了“蝴蝶效應”式的的線上Bug和故障 技術(shù)方面,通用依賴升級帶來的改動和回歸成本巨大,涉及例如Zent組件、中臺組件等依賴包相關(guān)的日常需求和技術(shù)升級幾乎不可推動 測試方面,單應用應對多人和多項目發(fā)布,單應用發(fā)布總和高且非常頻繁,每次的集成測試都有沖突處理和新問題暴露的風險 組織方面,單應用也無法很好應對業(yè)務小組的開發(fā)組織形式,邊界職責不清晰且模塊開發(fā)易干擾 架構(gòu)方面,前端無法和后端形成對應的領域應用開發(fā)模式,不利于業(yè)務的下沉,也無法支持前端能力的服務化和對技術(shù)棧的演進依賴
總體來說,臃腫的單體應用模式,給開發(fā)人員帶來了無法忍受的難處,給快速支撐業(yè)務帶來了很大的瓶頸,也沒有信心應對接下來的業(yè)務的繼續(xù)拓展。對美業(yè)PC進行架構(gòu)調(diào)整就是非常迫切和有價值的事情了
目標
業(yè)務架構(gòu)層面,圍繞美業(yè)PC的業(yè)務形態(tài)、項目架構(gòu)以及發(fā)展趨勢,將大型多團隊協(xié)同開發(fā)的前端應用視為多個獨立團隊所產(chǎn)出功能的組合。 技術(shù)架構(gòu)層面,解耦大型前端應用,拆分成基座應用、微前端內(nèi)核、注冊中心、若干獨立開發(fā)部署的子系統(tǒng),形成分布式體系的中心化治理系統(tǒng)。 軟件工程方面,保證漸進式遷移和改造,保證新老應用的正常運行。
達成價值
業(yè)務價值
實現(xiàn)了前端為維度的產(chǎn)品的原子化,如果整合新業(yè)務,子應用可以快速被其他業(yè)務集成 以業(yè)務領域劃分,讓組織架構(gòu)調(diào)整下的項目多人協(xié)作更職責清晰和成本低,且適應組織架構(gòu)調(diào)整 減慢系統(tǒng)的熵增,鋪平業(yè)務發(fā)展道路。
工程價值
實現(xiàn)了業(yè)務子應用獨立開發(fā)和部署,構(gòu)建部署的等待耗時從15分鐘降到了1分半 支持漸進式架構(gòu),系統(tǒng)子應用之間依賴無關(guān),可以單個升級依賴,技術(shù)棧允許不一致,技術(shù)迭代的空間更大 前端能力能夠服務化輸出 架構(gòu)靈活,新的業(yè)務可以在不增加現(xiàn)存業(yè)務開發(fā)人員認知負擔的前提下,自由生長無限拓展
缺點
一個架構(gòu)的設計其實對整體的一個權(quán)衡和取舍,除了價值和優(yōu)勢之外,也帶來一些需要去考慮的影響。

Part 02 架構(gòu)與工程
從全局視角把握成果
微前端方案有哪些
使用 HTTP 服務器反向代理到多個應用 在不同的框架之上設計通訊、加載機制 通過組合多個獨立應用、組件來構(gòu)建一個單體應用 使用 iFrame 及自定義消息傳遞機制 使用純 Web Components 構(gòu)建應用 結(jié)合 Web Components 構(gòu)建

每種方案都有自己的優(yōu)劣,我們兄弟團隊采用了最原始的網(wǎng)關(guān)轉(zhuǎn)發(fā)配置類似 Nginx 配置反向代理,從接入層的角度來將系統(tǒng)組合,但是每一次新增和調(diào)整都需要在運維層面去配置。
而 iframe 嵌套是最簡單和最快速的方案,但是 iframe的弊端也是無法避免的。
Web Components的方案則需要大量的改造成本。
組合式應用路由分發(fā)方案改造成本中等且滿足大部分需求,也不影響個前端子應用的體驗,是當時比較先進的一種方案。
架構(gòu)設計選型注意點
如何降低系統(tǒng)的復雜度? 如何保障系統(tǒng)的可維護性? 如何保障系統(tǒng)的可拓展性? 如何保障系統(tǒng)的可用性? 如何保障系統(tǒng)的性能?
綜合評估之后我們選用了組合式應用路由分發(fā)方案,但是仍然有架構(gòu)整體藍圖和工程實現(xiàn)需要去設計。
需求分析
子應用獨立運行/部署 中心控制加載(服務發(fā)現(xiàn)/服務注冊) 子應用公用部分復用 規(guī)范子應用的接入 基座應用路由和容器管理 建立配套基礎設施
設計原則
支持漸進式遷移,平滑過渡 拆分原則統(tǒng)一,嘗試領域劃分來解耦
應用架構(gòu)圖

系統(tǒng)拆分

這里拆分需要說明三個點:
獨立部署(服務注冊):上傳應用資源包(打包生成文件)到Apollo配置平臺,是一個點睛之筆 服務化和npm包插件化的區(qū)別是不需要通過父應用構(gòu)建來集成,彼此依賴無關(guān),發(fā)布獨立,更加靈活/可靠 同時 Apollo 承載了注冊中心的功能,可以省去子應用的web服務器的這一層,簡化了架構(gòu)
時序圖

前端流程圖

## Part 03 關(guān)鍵技術(shù)
落地中有哪些值得一提的技術(shù)細節(jié)
關(guān)鍵技術(shù)一覽
我們按項目拆分來結(jié)構(gòu)化講述,有架構(gòu)核心、注冊中心、子應用、代碼復用四篇。
其中包含了這些技術(shù)點:
Apollo Apollo Cli Version Manage Sandbox RouterMonitor MicroPageLoader Shared Menu Shared Common
[架構(gòu)核心]消息通信




[架構(gòu)核心]路由分發(fā)

當瀏覽器的路徑變化后,最先接受到這個變化的是基座的router,全部的路由變化由基座路由 RouterMonitor 掌管,因為它會去劫持所有引起url變化的操作,從而獲取路由切換的時機。如果是apps/xxx/#之前的變化,只會攔截阻止瀏覽器再次發(fā)起網(wǎng)頁請求不會下發(fā),沒有涉及#之前的url變化就下發(fā)到子應用,讓子應用路由接管。
[架構(gòu)核心]應用隔離
主要分為 JavaScript執(zhí)行環(huán)境隔離 和 CSS樣式隔離。
JavaScript 執(zhí)行環(huán)境隔離:每當子應用的JavaScript被加載并運行時,它的核心實際上是對全局對象 window 的修改以及一些全局事件的的改變,例如 JQuery 這個js運行之后,會在 window 上掛載一個 window.$ 對象,對于其他庫 React、Vue 也不例外。為此,需要在加載和卸載每個子應用的同時,盡可能消除這種沖突和影響,最普遍的做法是采用沙箱機制 SandBox。
沙箱機制的核心是讓局部的 JavaScript 運行時,對外部對象的訪問和修改處在可控的范圍內(nèi),即無論內(nèi)部怎么運行,都不會影響外部的對象。通常在 Node.js 端可以采用 vm 模塊,而對于瀏覽器,則需要結(jié)合 with 關(guān)鍵字和 window.Proxy 對象來實現(xiàn)瀏覽器端的沙箱。
CSS 樣式隔離:當基座應用、子應用同屏渲染時,就可能會有一些樣式相互污染,如果要徹底隔離 CSS 污染,可以采用 CSS Module 或者命名空間的方式,給每個子應用模塊以特定前綴,即可保證不會相互干擾,可以采用 webpack 的 postcss 插件,在打包時添加特定的前綴。
對于子應用與子應用之間的CSS隔離就非常簡單,在每次應用加載是,就將改應用所有的 link 和 style 內(nèi)容進行標記。在應用卸載后,同步卸載頁面上對應的 link 和 style 即可。
[架構(gòu)核心]核心流程圖
我們把路由分發(fā)、應用隔離、應用加載、通用業(yè)務邏輯收納到到了微前端內(nèi)核的二方包中,用作各個業(yè)務線復用,在內(nèi)部達成統(tǒng)一約定。

[注冊中心]Apollo
其實大部分公司在落地微前端方案的時候,并有沒所謂的注冊中心的概念。為什么我們的微前端也會有注冊中心這個概念和實際存在呢?選型的思考點也主要來自我們后端的微服務架構(gòu)。

為什么選擇引入注冊中心增加整體架構(gòu)的復雜度?
兩個原因:
我們的子應用之間雖然不需要通信,但是也存在基座應用需要所有子應用的資源信息的情況,用來維護路由對應子應用資源地址的映射。大部分公司落地時候,都把子應用的地址信息硬編碼到了基座。這樣子應用增刪改時候,就需要去重新部署基座應用,這違背了我們解耦的初衷。注冊中心把這份映射文件從基座剝離出來了,讓架構(gòu)具備了更好的解耦和柔性。 要知道我們的子應用的產(chǎn)物入口是 hash 化的上傳到 CDN 的 JS 文件,同時避免子應用發(fā)布也需要發(fā)布基座應用。有兩個解決方案,一種是增加子應用的 Web 服務器,可以通過固定的 HTTP 服務地址拿到最新的靜態(tài)資源文件。一種就是增加注冊中心,子應用發(fā)布就是推送新的 JS地址給到 注冊中心,子應用的架構(gòu)就可以更薄。
需要一個注冊中心的話,我們也有兩種方案,一種是自己自研一個專門服務于自己的微前端,雖然可以更加貼合和聚焦,但是作為注冊中心,高可用的技術(shù)底層要求下的熔斷降級等機制必不可少,這些研發(fā)難度大成本也高。還有一種是直接應用成熟的提供注冊中心能力的開源項目或者依賴公司的已經(jīng)存在的技術(shù)設施組件。
最后我們確定在選用公司內(nèi)部的基礎技術(shù)設施的 Apollo 項目,優(yōu)勢有這么兩方面。
項目本身開源,成熟程度很高,在多環(huán)境、即時性、版本管理、灰度發(fā)布、權(quán)限管理、開放API、支持端、簡單部署等功能性方面做得很不錯,是一個值得信賴的高可用的配置中心。 公司內(nèi)部針對做了私有化定制和部署,更加適配業(yè)務,并且在 Java 和 Node 場景下都有穩(wěn)定和使用,有維護人員值班。

子應用的打包構(gòu)建體驗
定位:一個子應用構(gòu)建完是一個帶 hash 的靜態(tài)資源,等待被基座加載。
怎么做:
打包一個單入口的靜態(tài)資源,同時暴露全局方法給基座 每次構(gòu)建生成帶 hash 的入口 app.js 獲取打包產(chǎn)出生成上傳配置 根據(jù)環(huán)境參數(shù)上傳到apollo 體驗如何
非常輕量,無須發(fā)布,構(gòu)建即可
子應用如何推送打包完成的 cdn 地址給 Apollo

獲取打包完成的產(chǎn)物的 JSON,獲取入口文件 Hash,和當前項目的基礎信息。 基于上述配置生成內(nèi)容,然后調(diào)用 Apollo 平臺開放的 API 上傳到 Apollo。
如何進行多環(huán)境發(fā)布及服務鏈協(xié)作

環(huán)境主要分為測試、預發(fā)、生產(chǎn)。 打包完成后,根據(jù)微前端構(gòu)建平臺指定環(huán)境。 推送配置時候,指定 Apollo 對應的環(huán)境集群就好了。 基座應用在運行時候,會根據(jù)環(huán)境與 Apollo 交互對應環(huán)境集群的注冊表信息。
[代碼復用]子應用之間如何復用公共庫
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
注:如果是新創(chuàng)建子應用 1-2-3-4 ;如果是去修改一個子引用 1-3-4
[代碼復用]使用shared需要注意什么
修改了 shared 的組件,需要 push 改動到 shared 倉庫 如果一個 shared 中的組件被某個子應用頻繁更新,可以考慮將這個組件從 shared 中移除,內(nèi)化到子應用中
[子應用]子應用如何接入
首先,我們需要明白我們對子應用的定位:
一個子應用構(gòu)建完后是一個帶 hash 的靜態(tài)資源,等待被基座加載,然后在中心渲染視圖,同時擁有自己的子路由
第一步,根據(jù)我們的模板新建一個倉庫,并置入對應子應用的代碼

第二步,接入shared以及修改一系列配置文件
第三步,進行開發(fā)所需要的轉(zhuǎn)發(fā)配置
第四部,運行,并嘗試打包部署
[子應用]子應用能獨立調(diào)式嗎?怎么基座應用聯(lián)調(diào)?
開啟基座,端口和資源映射到本地再調(diào)式 Zan-proxy 本地 Nginx 轉(zhuǎn)發(fā)
[子應用]子應用開發(fā)體驗

Part 04 項目實施
一個問題從出現(xiàn)到被解決走過的曲折道路
1.立項前的心路
看過微前端這個概念,覺得花里胡哨,玩弄名詞,強行造出新概念。 對項目的目前出現(xiàn)的問題有個大概感知(是個問題) 從業(yè)務出發(fā)利用現(xiàn)有知識背景思考解決手段(幾乎無解) 回想了解過微前端架構(gòu)的概念和場景,感受到兩者有契合(人生若只如初見) 參考行業(yè)的解決方案印證,決定用微前端來脫掉膨脹的包袱(原來是該拆了) 首先把項目在前端架構(gòu)優(yōu)化理了一遍,輸出架構(gòu)圖(項目整體上探路) 接下來梳理各個業(yè)務模塊的依賴,看下有哪些(子應用分析) 大量和不同人的聊天、了解、討論,獲取支撐技術(shù)選型的信息(外界專家) 確定微前端架構(gòu)在美業(yè)下的落地基本模型(架構(gòu)基本) 進行概要技術(shù)設計(具象化) 明確迭代范圍 技術(shù)評審 拉幫結(jié)伙/分工 kickoff 然而故事才剛剛開始…
2.參考微前端資料

3.進行PC架構(gòu)優(yōu)化計劃



4.風險
預知
開發(fā)人員投入度不足 技術(shù)上的不確定性來更多工期風險 細節(jié)的技術(shù)實現(xiàn)需要打磨耗時超出預期 部分功能難以實現(xiàn)
意外
對項目架構(gòu)理解不準確 任務拆分和邊界理解不到位 測試人員投入不足 協(xié)作摩擦
5.迭代立項

6.進展
PC微前端基座應用已上線 PC數(shù)據(jù)拆分成子應用已上線 協(xié)調(diào)中臺前端抽取了美業(yè)微前端內(nèi)核 通用工具方法和枚舉的可視化 搭配Apollo平臺形成了前端子應用資源的注冊中心 子應用接入文檔輸出 若干前端技術(shù)體系的優(yōu)化
7.后續(xù)計劃

關(guān)于本文
作者:邊城到此莫若
https://segmentfault.com/a/1190000040106401
我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關(guān)的交流、學習、共建。下方加 考拉 好友回復「Node」即可。

???“分享、點贊、在看” 支持一波??
