微前端在美團外賣的實踐(美團微前端架構)
微前端是微服務理念在前端的應用。之前給大家介紹過微前端在美團HR系統(tǒng)和美團閃購的實踐文章。今天的文章來自美團外賣廣告團隊,他們參考業(yè)界優(yōu)秀方案,同時也深度結合了廣告端實際業(yè)務的情況,提出了基于React的中心路由基座式微前端方案。
背景
微前端是一種利用微件拆分來達到工程拆分治理的方案,可以解決工程膨脹、開發(fā)維護困難等問題。隨著前端業(yè)務場景越來越復雜,微前端這個概念最近被提起得越來越多,業(yè)界也有很多團隊開始探索實踐并在業(yè)務中進行了落地。可以看到,很多團隊也遇到了各種各樣的問題,但各自也都有著不同的處理方案。誠然,任何技術的實現(xiàn)都要依托業(yè)務場景才會變得有意義,所以在闡述美團外賣廣告團隊的微前端實踐之前,我們先來簡單介紹一下外賣商家廣告端的業(yè)務形態(tài)。目前,我們開發(fā)和維護的系統(tǒng)主要包括三端:
- PC系統(tǒng):單門店投放系統(tǒng)PC端
- H5系統(tǒng):單門店投放系統(tǒng)H5端
- KA系統(tǒng):多門店投放系統(tǒng)PC端
如上圖所示,原始解決方案的三端由各自獨立開發(fā)和維護,各自包含所有的業(yè)務線,而我們的業(yè)務開發(fā)情況是:
- PC端和H5端相同業(yè)務線的基本業(yè)務邏輯一致,UI差異大。
- PC端和KA端相同業(yè)務線的部分業(yè)務邏輯一致,UI差異小。
在這種特殊的業(yè)務場景下,就會出現(xiàn)一個有關開發(fā)效率的抉擇問題。即我們希望能復用的部分只開發(fā)一次,而不是三次。那么接下來,就有兩個問題擺在我們面前:
- 如何進行物理層面的復用(不同端的代碼在不同地址的Git倉庫)。
- 如何進行邏輯層面的復用(不同端的相同邏輯如何使用一份代碼進行抽象)。
我們這里重點看一下物理層面的復用,即:如何在物理空間上使得各自獨立的三端系統(tǒng)(不同倉庫)引入我們的復用層?我們嘗試了NPM包、Git subtree等類“共享文件”的方式后發(fā)現(xiàn),最有效率的復用方式是把三個系統(tǒng)放在一個倉庫里,去消除物理空間上的隔離,而不是去連接不同的物理空間。當然,我們?nèi)讼到y(tǒng)的技術棧是一致的,所以就進行了如下圖的改造:
可以看到,當我們把三端系統(tǒng)放在一個倉庫中時,通過common文件夾提供了物理層面可復用的土壤,不再需要“共享文件”式地進行頻繁地拉取操作,直接引用復用即可。不過,在帶來物理層面復用效率提升的同時,也加速了整個工程出現(xiàn)了爆炸式發(fā)展的問題,隨著產(chǎn)品線從最初的幾個發(fā)展到現(xiàn)在的幾十個之多,工程管理成本也在迅速增長。具體來說,包括如下四個方面:
- 新業(yè)務線產(chǎn)品急速增加,同時為了保證三端系統(tǒng)復用效率的最大化,把文件放入同一倉庫管理,導致文件數(shù)量增長極快,管理及協(xié)同開發(fā)難度也在不斷加大。
- 文件越來越多,文件結構越不受控制,業(yè)務開發(fā)尋址變得越來越困難。
- 文件越來越多,開發(fā)、構建、部署速度變得越來越慢,開發(fā)體驗在持續(xù)下降。
- 不同業(yè)務線間沒有物理隔離,出現(xiàn)了跨業(yè)務線互相引用混亂,例如A業(yè)務線出現(xiàn)了B業(yè)務線名字的組件。
如下圖所示,具體地說明了原有架構存在的問題。為了要解決這些問題,我們意識到需要拆分這些應用,即進行工程優(yōu)化的常規(guī)手段進行“分治”。那么要怎么拆呢?自然而然地我們就想到了微前端的概念。也從這個概念出發(fā),我們參考業(yè)界優(yōu)秀方案,同時也深度結合了廣告端實際業(yè)務的開發(fā)情況,對現(xiàn)有工程進行了微前端的實踐與落地。
需求分析
結合現(xiàn)有工程的狀況,我們進行了深度的分析。不過,在進行微前端方案確定前,我們先確定了需求點及期望收益,如下表所示:
方案選擇
經(jīng)過以上的需求分析,我們調研了業(yè)界及公司周邊的微前端方案,并總結了以下幾種方案以及它們各自主要的特點:
- NPM式:子工程以NPM包的形式發(fā)布源碼;打包構建發(fā)布還是由基座工程管理,打包時集成。
- iframe式:子工程可以使用不同技術棧;子工程之間完全獨立,無任何依賴;基座工程和子工程需要建立通信機制;無單頁應用體驗;路由地址管理困難。
- 通用中心路由基座式:子工程可以使用不同技術棧;子工程之間完全獨立,無任何依賴;統(tǒng)一由基座工程進行管理,按照DOM節(jié)點的注冊、掛載、卸載來完成。
- 特定中心路由基座式:子業(yè)務線之間使用相同技術棧;基座工程和子工程可以單獨開發(fā)單獨部署;子工程有能力復用基座工程的公共基建。
通過對各個方案特點進行分析,我們將重點關注項進行了對比,如下表所示:
經(jīng)過上面的調研對比之后,我們確定采用了特定中心路由基座式的開發(fā)方案,并命名為:基于React的中心路由基座式微前端。這種方案的優(yōu)點包括以下幾個方面:
- 保證技術棧統(tǒng)一在React。
- 子工程之間開發(fā)互相獨立,互不影響。
- 子工程可單獨打包、單獨部署上線。
- 子工程有能力復用基座工程的公共基建。
- 保持單頁應用的體驗,子工程之間切換不刷新。
- 改造成本低,對現(xiàn)有工程侵入度較低,業(yè)務線遷移成本也較低。
- 開發(fā)子工程和原有開發(fā)模式基本沒有不同,開發(fā)人員學習成本較低。
微前端實踐概覽
通過對方案的分析及技術方向上的梳理,我們確定了微前端的整體方案,如下圖所示:
可以看到,整個方案非常簡單明確,即按照業(yè)務線進行了路由級別的拆分。整個系統(tǒng)可分為兩個部分:
- 基座工程:用于管理子工程的路由切換、注冊子工程的路由和全局Store層、提供全局庫和復用層。
- 子工程:用于開發(fā)子業(yè)務線業(yè)務代碼,一個子工程對應一個子業(yè)務線,并且包含三端代碼和復用層代碼。
基座工程和子工程聯(lián)系起來的橋梁則是子工程的入口文件地址和路由地址的映射信息。這些映射信息可以讓基座工程準確地發(fā)現(xiàn)子工程資源的路徑從而進行加載。
微前端架構下的業(yè)務變化
經(jīng)過微前端實踐的改造,我們的業(yè)務在結構上發(fā)生了如下的變化:
如上圖所示,我們進行了微前端式的業(yè)務線拆分:
- 原有的PC系統(tǒng)、H5系統(tǒng)、KA系統(tǒng)分別改造成了PC基座系統(tǒng)、H5基座系統(tǒng)和KA基座系統(tǒng)。
- 原有的子業(yè)務線被拆分成了單獨的子倉庫,成為了業(yè)務線子工程(上圖中6個黑框豎列)。
- 業(yè)務線子工程分別包含PC端、H5端、KA端以及該業(yè)務線復用層的代碼(上圖中3個純色背景橫列)。
新的拆分使得子工程能夠按照業(yè)務線進行劃分,獨立維護。在解決復用層的同時保證了子工程大小可控,即子工程只有單個業(yè)務線的代碼。而單個業(yè)務線的復雜度并不高,也降低了工程維護的復雜度。
采用微前端拆分的方案,使得我們的業(yè)務不僅在縱向上保有了復用的能力,更重要的是擁有了橫向擴展的能力,無論產(chǎn)品業(yè)務線如何膨脹,我們都可以更輕松地應對。那么為了實現(xiàn)以上的能力,我們做了哪些工作呢?下文我們會詳細進行說明。
基于React技術棧的中心路由基座式微前端
微前端拆分的方案,我們命名為:基于React技術棧的中心路由基座式微前端。在具體實現(xiàn)上,我們會分為動態(tài)化方案、路由配置信息設計、子工程接口設計、復用方案設計和流程方案設計等幾個模塊來逐一進行說明。
動態(tài)化方案
首先,我們需要路由的管理方案,使得子工程之間有能力互通切換。其次,我們需要Store層的方案,讓子工程有能力使用全局Store。并且,我們還需要CSS的加載方案,來加載子工程的樣式布局。下面來詳細說明這三個方案。
動態(tài)路由
動態(tài)路由方案是想要進行路由級別的拆分,首先我們要確定用什么來管理路由?很多實現(xiàn)方案傾向于使用特制路由來管理模塊。例如開源框架Single-Spa,實現(xiàn)了自己的一套路由監(jiān)聽來切換子工程,并且需要子工程實現(xiàn)特定的注冊、掛載、卸載等接口來完成子工程和基座工程的動態(tài)對接,還需要特定的模塊管理系統(tǒng),例如systemjs來輔助完成這一過程。毋庸置疑,這對我們原有工程的改造成本很大,還需要添加額外庫,進而造成包體積大小上的開銷。并且子工程的開發(fā)者需要熟悉這些特定的接口,學習成本也比較高。顯然,這對于我們的業(yè)務場景和需求來說很不劃算。
那么,我們選擇什么來做路由管理呢?最終我們使用了React-Router,這樣能夠保持我們原來的技術棧不變,同時對于工程的侵入也是最低,幾乎可以忽略不計。此外,React-Router完全可以滿足我們的需求,而且自動會幫助我們管理頁面的加載與卸載,而不是每次切換路由都重新初始化整個子應用,所以在加載速度體驗上也是最優(yōu)的,跟單頁應用的體驗一致。
在實現(xiàn)上也很簡單,如下圖所示:
上面這個流程圖,展示了我們在基座工程中切換到子工程路由時,加載子工程并進行展示的過程。這里的重點步驟是加載子工程入口文件,并動態(tài)注冊子工程路由的過程。由于我們使用的是React-Router,顯然要使用其提供的動態(tài)能力來完成。這一過程也非常輕量,由于React-Router從版本4開始有了“破壞級”的升級,于是我們就調研了兩種方式進行動態(tài)加載路由(目前我們使用的是React-Router版本5),如下表所示:
React-Router版本3中,實現(xiàn)的基本代碼思路如下:
// react-router V3 用于接收子工程的路由export default () => ( <Route path="/subapp" getChildRoutes={(location: any, cb: any) => { const { pathname } = location.location; // 取路徑中標識子工程前綴的部分, 例如 '/subapp/xxx/index' 其中xxx即路由唯一前綴 const id = pathname.split('/')[2]; const subappModule = (subAppMapInfo as any)[id]; if (subappModule) { if (subappRoutes[id]) { // 如果已經(jīng)加載過該子工程的模塊,則不再加載,直接取緩存的routes cb(null, [subappRoutes[id]]); return; } // 如果能匹配上前綴則加載相應子工程模塊 currentPrefix = id; loadAsyncSubapp(subappModule.js) .then(() => { // 加載子工程完成 cb(null, [subappRoutes[id]]); }) .catch(() => { // 如果加載失敗 console.log('loading failed'); }); } else { // 可以重定向到首頁去 goBackToIndex(); } }} />);
而在React-Router版本4中,實現(xiàn)的基本代碼思路如下:
export const AyncComponent: React.FC<{ hotReload?: number; } & RouteComponentProps> = ({ location, hotReload }) => { // 子工程資源是否加載完成 const [ayncLoaded, setAyncLoaded] = useState(false); // 子工程url配置信息是否加載完成 const [subAppMapInfoLoaded, setSubAppMapInfoLoaded] = useState(false); const [ayncComponent, setAyncComponent] = useState(null); const { pathname } = location; // 取路徑中標識子工程前綴的部分, 例如 '/subapp/xxx/index' 其中xxx即路由唯一前綴 const id = pathname.split('/')[2]; useEffect(() => { // 如果沒有子工程配置信息, 則請求 if (!subAppMapInfoLoaded) { fetchSubappUrlPath(id).then((data) => { subAppMapInfo = data; setSubAppMapInfoLoaded(true); }).catch((url: any) => { // 失敗處理 goBackToIndex(); }); return; } const subappModule = (subAppMapInfo as any)[id]; if (subappModule) { if (subappRoutes[id]) { // 如果已經(jīng)加載過該子工程的模塊,則不再加載,直接取緩存的routes setAyncLoaded(true); setAyncComponent(subappRoutes[id]); return; } // 如果能匹配上前綴則加載相應子工程模塊 // 如果請求成功,則觸發(fā)JSONP鉤子window.wmadSubapp currentPrefix = id; setAyncLoaded(false); const jsUrl = subappModule.js; loadAsyncSubapp(jsUrl) .then(() => { // 加載子工程完成 setAyncComponent(subappRoutes[id]); setAyncLoaded(true); }) .catch((urlList) => { // 如果加載失敗 setAyncLoaded(false); console.log('loading failed...'); }); } else { // 可以重定向到首頁去 goBackToIndex(); } }, [id, subAppMapInfoLoaded, hotReload]); return ayncLoaded ? ayncComponent : null;};
可以看到,這種方式實現(xiàn)起來非常簡單,不需要額外依賴,同時滿足了我們“拆分”的訴求。
動態(tài)Store
對于Store層,我們原工程使用的是Redux,子工程通過路由動態(tài)注冊進來天然就可以訪問到全局Store,所以對于Store的訪問能夠自動支持。那么,如果子工程想要注冊自己的全局Store該怎么辦呢?而且我們還用了redux-saga來作為異步處理方案。redux-saga如何動態(tài)注冊呢?還是利用它們各自的API就可以達到我們的目的?從下圖中可以看到,支持動態(tài)Store也是花費很小的改造成本就可以完成。
動態(tài)CSS
同樣的對應子工程的樣式布局,我們也需要通過某種途徑加載到基座工程中來。這個很自然地用異步加載CSS文件通過style標簽注入來完成,不過這里需要注意兩個問題:
一個問題是,加載子工程的JS入口文件和CSS文件可以同時發(fā)起請求,但是需要保證CSS文件加載完成后再進行JS入口文件的路由注冊。因為如果路由先注冊了頁面就會顯示出來,如果這時CSS文件還沒有加載完畢,就會出現(xiàn)頁面樣式閃動的問題。我們通過先加載CSS再加載JS的策略來避免這個問題的發(fā)生。
另一個問題是,怎么保證子工程的CSS不會和其他子工程沖突。我們利用PostCSS插件在編譯子工程時,按照分配給子工程的唯一業(yè)務線標識,為每一組CSS規(guī)則生成了命名空間來解決這個問題。而子業(yè)務線開發(fā)者是沒有感知的,可以沒有“心智負擔”地書寫子工程的樣式。
路由配置信息方案
在動態(tài)加載方案確定之后,基座工程怎么才能知道子工程的資源路徑,進而加載對應的JS和CSS資源呢?我們需要一組映射信息。如下圖所示,業(yè)務線唯一標識為Key,相應的靜態(tài)資源地址為Value。這樣的話,當基座工程切換到子工程時就可以拉取這個配置信息,在路由切換時準確地找到對應的子工程,進而進行后續(xù)的資源加載過程。這里可能會遇到的一個問題,即如果JS和CSS過大,是否能進行拆分?
根據(jù)我們業(yè)務的實際情況,目前靜態(tài)資源的大小是可控的,無需注冊多個,單一入口地址完全能夠滿足我們的業(yè)務需求,并且由于我們的改造完全基于現(xiàn)有技術棧。如果業(yè)務很復雜,完全可以在子工程中通過webpack的動態(tài)import進行路由懶加載,也就是說,子工程完全可以按照路由再次切分成chunks來減少JS的包體積。至于CSS本身就很小,長期也不會有進行切分的需要。
子工程接口方案
子工程需要暴露它要注冊給基座工程的對象,來進行基座工程加載子工程的過程。在子工程入口文件中定義registerApp來傳遞注冊的對象,主要代碼如下:
import reducers from 'common/store/labor/reducer';import sagas from 'common/store/labor/saga';import routes from './routes/index';function registerApp(dep: any = {}): any { return { routes, // 子工程路由組件 reducers, // 子工程Redux的reducer sagas, // 子工程的Redux副作用處理saga };}export default registerApp
我們這里暴露了子工程的三個對象:這里最重要的就是routes路由組件,就是在寫React-Router(版本4及以上)的路由。子工程開發(fā)者只需要配置routes對象即可,沒有任何學習成本,其代碼如下:
/** * 子工程路由注冊說明 * 如注冊的路由如下: * path: 'index' * 路由前綴會被追加上,路由前綴規(guī)則見變量urlPrefix * 在主工程的訪問路勁為:/subapp/${工程注冊名稱}/index */const urlPrefix = `/subapp/${microConfig.name}/`;const routes = [ { path: 'index', component: IndexPage, },];const AppRoutes = () => ( <Switch> { routes.map(item => ( <Route key={item.path} exact path={`${urlPrefix}${item.path}`} component={item.component} /> )) } <Redirect to="/" /> </Switch>);export default AppRoutes;
除了上方的routes對象,還剩下兩個接口對象是:reducers和sagas,用于動態(tài)注冊全局Store相關的數(shù)據(jù)和副作用處理。這兩個接口我們在子工程中暫時沒有開放,因為按照業(yè)務線拆分過后,由于業(yè)務線間獨立性很強,全局Store的意義就不大了。我們希望子工程可以自行處理自己的Store,即每個業(yè)務線維護自己的Store,這里就不再展開進行說明了。
復用方案
基座工程除了路由管理之外,還作為共享層共享全局的基建,例如框架基本庫、業(yè)務組件等。這樣做的目的是,子業(yè)務線間如果有相同的依賴,切換的時候就不會出現(xiàn)重復加載的問題。例如下面的代碼,我們把React相關庫都以全局的方式導出,而子工程加載的時候就會以external的形式加載這些庫,這樣子工程的開發(fā)者不需要額外的第三方模塊加載器,直接引用即可,和平時開發(fā)React應用一致,沒有任何學習成本。而和各個業(yè)務都相關的公用組件等,我們會放到wmadMicro的全局命名空間下進行管理。主要代碼如下:
import * as React from 'react';import * as ReactDOM from 'react-dom';import * as ReactRouterDOM from 'react-router-dom';import * as Axios from 'axios';import * as History from 'history';import * as ReactRedux from 'react-redux';import * as Immutable from 'immutable';import * as ReduxSagaEffects from 'redux-saga/effects';import Echarts from 'echarts';import ReactSlick from 'react-slick';function registerGlobal(root: any, deps: any) { Object.keys(deps).forEach((key) => { root[key] = deps[key]; });}registerGlobal(window, { // 在這里注冊暴露給子工程的全局變量 React, ReactDOM, ReactRouterDOM, Axios, History, ReactRedux, Immutable, ReduxSagaEffects, Echarts, ReactSlick,});export default registerGlobal;
流程方案
在確定了程序拆分運行的整體銜接之后,我們還要確定開發(fā)方案、部署方案以及回滾方案。我們?nèi)绾伍_始開發(fā)一個子工程?以及我們?nèi)绾尾渴鹞覀兊淖庸こ蹋?/p>
開發(fā)流程
有兩種開發(fā)方案可以滿足獨立開發(fā)的目的:第一種是提供一個基座工程的Dev環(huán)境,子工程在本地啟動后在Dev環(huán)境進行開發(fā),這種開發(fā)方式要求有一套基座工程的更新機制,例如基座工程更新后要同步部署到Dev環(huán)境。第二種是子工程開發(fā)者拉取基座工程到本地并啟動本地開發(fā)環(huán)境,然后拉取子工程到本地,再啟動子工程本地開發(fā)環(huán)境進行開發(fā),這種開發(fā)方式是目前我們使用的方式。如下圖所示,我們提供了子工程腳手架來快速創(chuàng)建子工程,開發(fā)者無需做任何配置和額外學習成本,就可以像開發(fā)React應用一樣進行開發(fā)。
熱更新
在開發(fā)過程中,我們希望我們的開發(fā)體驗和開發(fā)單頁應用的體驗一致,也要支持熱更新。由于我們的拆分,實際上有兩個服務,即基座和子工程,所以我們以上圖的方式完成了熱更新的支持:在子工程的module.hot中通過再次觸發(fā)基座工程中的JSONP鉤子來通知基座工程,來再次觸發(fā)renderApp達到子工程更新代碼則頁面熱刷新的目的。主要代碼如下:
// 在子工程入口文件import routes from './routes/index';function registerApp(dep: any = {}): any { return { routes, };}if ((module as any).hot) { (module as any).hot.accept('./routes/index', (): any => { window.wmadSubapp(registerApp, true); // 支持子工程熱加載的信息傳遞 });}export default registerApp
Mock數(shù)據(jù)
子工程目前Mock數(shù)據(jù)的方式有三種:一是在基座本地Mock,這種Mock方式天然支持,因為基座工程基于外賣工程化Nine腳手架進行開發(fā),本身支持本地Mock。二是支持子工程本地Mock。三是使用公共Mock服務YAPI。目前子工程開發(fā)的Mock功能結合第一種方式和第三種方式進行。
部署方案
最后是部署方案,我們達成了獨立部署上線的目的,即子工程發(fā)布不需要基座工程的參與。之前所有子業(yè)務線都在一個工程中,打包速度隨著業(yè)務線的膨脹變得越來越慢,而如下的方案使得子工程的開發(fā)和部署完全獨立,單個業(yè)務線的打包速度會非???,從之前的分鐘級別降到了秒級別。如下圖所示,子工程部署只需要把子工程打包,并在上傳CDN之后,把配置信息更新即可,因為配置信息中有子工程新的資源地址,這樣就達到了發(fā)布上線的目的。
整個部署過程我們是托管到Talos(美團內(nèi)部自研的部署工具)上的,配置信息我們是托管到Portm(美團內(nèi)部自研的文件存儲)上的(通過我們開發(fā)的Talos的插件UpdatePubInfo-To-Portm來更新我們的配置信息)。在靜態(tài)資源上傳到CDN之后,就可以更新配置信息,供主工程調用,也就完成了子工程上線的過程。利用美團現(xiàn)有服務,我們很迅速地完成了子工程單獨部署上線的整個流程。
回滾方案
在部署方案中,我們通過Talos進行部署,它本身就帶有回滾功能。得益于子工程的發(fā)布和普通工程的發(fā)布并沒什么本質不同,都是將靜態(tài)資源放置到CDN上,通過靜態(tài)資源的的contenthash值來區(qū)分不同版本,所以回滾的時候,Talos取到上個版本(或者某個前版本)的靜態(tài)資源,再通過Portm更新我們的配置信息即可完成。整個過程和普通工程沒有區(qū)別,發(fā)版人員只需簡單地點下回滾按鈕即可。
監(jiān)控方案
改變了原有的開發(fā)模式后,我們還對幾個關鍵節(jié)點進行了監(jiān)控報警的埋點。利用美團CAT(已經(jīng)在GitHub上開源)和天網(wǎng)(美團內(nèi)部的監(jiān)控系統(tǒng)),我們分別在子工程的配置信息、靜態(tài)資源加載等節(jié)點上進行了埋點上報,統(tǒng)計子工程加載成功率,及時發(fā)現(xiàn)可能出現(xiàn)的子工程切換問題。具體情況如下圖所示:
上方左圖是按照端維度進行統(tǒng)計的示例,上方右圖是PC端按照產(chǎn)品線統(tǒng)計加載成功數(shù)的示例。默認都是統(tǒng)計當天的數(shù)據(jù),顯示‘-’的表明當前沒有數(shù)據(jù)。對資源加載的監(jiān)控目前有三種類型:JSON、JS和CSS,資源加載失敗的統(tǒng)計也包含這三種類型。天網(wǎng)的監(jiān)控按照分鐘級進行,每分鐘內(nèi)如果有加載失敗就會發(fā)出報警,偶爾的報警可能是用戶網(wǎng)絡的問題,如果出現(xiàn)大批量的報警就要引起重視了。
總結
以上就是微前端在外賣商家廣告端的實踐過程??偟膩碚f,我們完成了以下的目標:
- 按照領域(業(yè)務線)拆分工程,工程的可維護性得到提高,相關領域進行了內(nèi)聚,無關領域進行了解耦。
- 子工程提供了PC、H5、KA三端的物理復用土壤,消除了工程膨脹問題,工程大小也變得可控。
- 子工程打包速度從分鐘級降為秒級,提高了開發(fā)體驗,加快了上線的速度。
- 子工程開發(fā)支持熱更新,開發(fā)體驗不降級。
- 子工程能夠單獨開發(fā)、單獨部署、單獨上線,業(yè)務線間互不影響。
- 整體工程改造成本低,插拔式開發(fā),無侵入式代碼,在正常業(yè)務開發(fā)的同時短期內(nèi)就可以完成上線。
- 開發(fā)者學習成本低,完整地保留了單頁應用開發(fā)的開發(fā)體驗,開發(fā)者可快速上手。
目前在美團廣告端,以微前端模式上線的子業(yè)務線已經(jīng)有很多個。另外還有多個正在開發(fā)的微前端子工程,剩余在主工程中的子業(yè)務線后續(xù)也可以無痛遷移出來成為子工程。我們內(nèi)部也在此過程中搜集了不少意見反饋,未來繼續(xù)在實踐中進行思考和完善。在此過程中,我們深知還有很多做得不夠完善甚至存在問題的地方,歡迎大家跟我們進行交流,幫我們提出寶貴意見或者給予指導。當然也歡迎大家加入我們團隊(文末有招聘信息),一起共建。
作者簡介
張嘯、魏瀟、天堯,均為美團外賣前端團隊研發(fā)工程師。
招聘信息
美團外賣廣告前端團隊誠招高級前端開發(fā)、前端開發(fā)專家。我們?yōu)樯碳姨峁┳儸F(xiàn)服務平臺,為用戶提供優(yōu)質廣告體驗,是外賣商業(yè)變現(xiàn)中的重要環(huán)節(jié)。歡迎各位小伙伴的加入,共同打造極致廣告產(chǎn)品。感興趣的同學可投遞簡歷至:tech@meituan.com(郵件標題注明:美團外賣廣告前端團隊)