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