作者:京東科技?陳云飛
一,需求背景
1 業(yè)務(wù)背景
在以往的業(yè)務(wù)場(chǎng)景中,用戶進(jìn)入五花八門的菜單體系中,往往會(huì)產(chǎn)生迷茫情緒,難以理解平臺(tái)名稱及具體作用,導(dǎo)致數(shù)據(jù)開發(fā)與管理學(xué)習(xí)成本較高,降低工作效率。為此我們整合從數(shù)據(jù)接入,數(shù)據(jù)開發(fā),數(shù)據(jù)管理的全鏈路流程,期望讓用戶體驗(yàn)一站式數(shù)據(jù)開發(fā)與管理的便捷性;并提供不同業(yè)務(wù)場(chǎng)景,方便根據(jù)業(yè)務(wù)場(chǎng)景進(jìn)行進(jìn)一步數(shù)據(jù)開發(fā)與管理工作,為數(shù)據(jù)應(yīng)用平臺(tái)打下夯實(shí)規(guī)范的數(shù)據(jù)基礎(chǔ),方便用戶在數(shù)據(jù)平臺(tái)里,對(duì)于數(shù)據(jù)開發(fā)和數(shù)據(jù)應(yīng)用進(jìn)行便捷性的切換,因此我們?cè)O(shè)計(jì)目前的門戶基座,可以快速瀏覽各個(gè)平臺(tái),同時(shí)串聯(lián)數(shù)據(jù)開發(fā)與管理的工作,減少用戶的試錯(cuò)成本,提升工作效率。
2 標(biāo)品需求
基座子-項(xiàng)目交互簡(jiǎn)圖如圖1;
1,基座的業(yè)務(wù)頁(yè)面比較簡(jiǎn)單,主要包含:頂部邊欄、左側(cè)邊欄、公共子菜單、頂級(jí)平臺(tái)菜單;
2,點(diǎn)擊左上角圖標(biāo),顯示頂級(jí)平臺(tái)菜單,點(diǎn)擊平臺(tái),在基座左側(cè)邊欄動(dòng)態(tài)顯示平臺(tái)一級(jí)菜單;
3,點(diǎn)擊基座左側(cè)邊欄,在公共子菜單,動(dòng)態(tài)顯示一級(jí)菜單下邊的二級(jí)、三級(jí)菜單;
4,點(diǎn)擊基座左側(cè)邊欄或者公共子菜單,需要基座調(diào)度,在子項(xiàng)目區(qū)域正確加載子項(xiàng)目及子項(xiàng)目頁(yè)面;
圖 1
數(shù)據(jù)中臺(tái)新門戶基座要接入老數(shù)據(jù)平臺(tái)一、老數(shù)據(jù)平臺(tái)二等 多平臺(tái)的前端項(xiàng)目,并且原有前端子項(xiàng)目在門戶基座呈現(xiàn)任意子項(xiàng)目、任意子項(xiàng)目頁(yè)面 任意混搭的需求;新門戶要接入的項(xiàng)目關(guān)系詳情如圖2;
圖 2
3 數(shù)據(jù)中臺(tái)融合;
數(shù)據(jù)中臺(tái)融合指的是京東體系內(nèi),其他對(duì)外獨(dú)立交付的數(shù)據(jù)中臺(tái),比如京東工業(yè)、京東城市等項(xiàng)目;數(shù)據(jù)中臺(tái)商業(yè)化的子項(xiàng)目不僅在新門戶容器內(nèi),也可以按需打包進(jìn)其他數(shù)據(jù)中臺(tái)容器;下面簡(jiǎn)稱 數(shù)據(jù)中臺(tái)融合;
二,微前端技術(shù)調(diào)研
原有數(shù)據(jù)中臺(tái)接入子應(yīng)用的方式有多種:iframe 嵌套、@weus 微應(yīng)用、鏈接跳轉(zhuǎn)等;
1 iframe 存在問(wèn)題:
? url 不同步。瀏覽器刷新 iframe url 狀態(tài)丟失、后退前進(jìn)按鈕無(wú)法使用。
? UI 不同步,DOM 結(jié)構(gòu)不共享。想象一下屏幕右下角 1/4 的 iframe 里來(lái)一個(gè)帶遮罩層的彈框,同時(shí)我們要求這個(gè)彈框要瀏覽器居中顯示,還要瀏覽器 resize 時(shí)自動(dòng)居中..
? 全局上下文完全隔離,內(nèi)存變量不共享。iframe 內(nèi)外系統(tǒng)的通信、數(shù)據(jù)同步等需求,主應(yīng)用的 cookie 要透?jìng)鞯礁蛎疾煌淖討?yīng)用中實(shí)現(xiàn)免登效果。
? 慢。每次子應(yīng)用進(jìn)入都是一次瀏覽器上下文重建、資源重新加載的過(guò)程。
2 weus 存在問(wèn)題:
?weus 是京東內(nèi)部研發(fā)已經(jīng)不再維護(hù)了,如果有新的問(wèn)題需要自己解決,對(duì)微前端有新需求也需要自己去實(shí)現(xiàn);
?weus 沒(méi)有嚴(yán)格的 css 沙箱、js 沙箱,而在我們的需求中,沙箱機(jī)制是剛需,我們要接入的子項(xiàng)目在 window 上掛在哪些變量,無(wú)法通過(guò)規(guī)范做到強(qiáng)有力的制約(因?yàn)橐尤氲捻?xiàng)目是已經(jīng)寫完了)
?weus 在微前端功能實(shí)現(xiàn),沒(méi)有qiankun 豐富健全,比如全局狀態(tài)管理、雖然 weus 實(shí)現(xiàn)了子應(yīng)用的預(yù)加載,但是比較機(jī)械,是把所有注冊(cè)的子應(yīng)用都緩存,實(shí)際可能不需要,qiankun 就比較靈活可以根據(jù)需要手動(dòng)緩存等;
3 鏈接跳轉(zhuǎn)
鏈接跳轉(zhuǎn),指的是點(diǎn)擊一個(gè)菜單,跳轉(zhuǎn)到另一個(gè)頁(yè)面。這種方式不符合 “一站式 ”大數(shù)據(jù)平臺(tái)產(chǎn)品定位;
4 最終結(jié)論
通過(guò)幾種實(shí)現(xiàn)方式的對(duì)比,最終決定以 qiankun 微前端為基礎(chǔ),結(jié)合我們的實(shí)際業(yè)務(wù)場(chǎng)景,通過(guò)權(quán)限菜單樹 和 子項(xiàng)目關(guān)聯(lián)來(lái)實(shí)現(xiàn)基座對(duì)子項(xiàng)目的調(diào)度,具體方案請(qǐng)參照 三,基座技術(shù)方案詳細(xì)描述;
三,技術(shù)方案詳細(xì)描述;
1 名詞解釋
跨子項(xiàng)目跳轉(zhuǎn)
指的是子項(xiàng)目沒(méi)通過(guò)基座自行跳轉(zhuǎn),基座此時(shí)需要根據(jù) url 匹配正確的基座顯示;
觸發(fā)節(jié)點(diǎn)
點(diǎn)擊菜單,菜單對(duì)應(yīng)的節(jié)點(diǎn)即為觸發(fā)節(jié)點(diǎn),跨子項(xiàng)目跳轉(zhuǎn)(刷新頁(yè)面)沒(méi)有觸發(fā)節(jié)點(diǎn),以跳轉(zhuǎn)后url 對(duì)應(yīng)的頁(yè)面節(jié)點(diǎn)為觸發(fā)節(jié)點(diǎn);
頁(yè)面節(jié)點(diǎn)
權(quán)限菜單樹中,掛載了子項(xiàng)目頁(yè)面的節(jié)點(diǎn)
門戶節(jié)點(diǎn)
狹義指的是,當(dāng)前頁(yè)面節(jié)點(diǎn)對(duì)應(yīng)的公共子菜單父級(jí)節(jié)點(diǎn);廣義指的是通過(guò)頁(yè)面節(jié)點(diǎn)向父系節(jié)點(diǎn)查詢,最終確認(rèn)公共子菜單、基座邊欄的顯示,并通過(guò)頁(yè)面節(jié)點(diǎn)確定菜單高亮等行為;
第一子系節(jié)點(diǎn)
指的是在由觸發(fā)節(jié)點(diǎn)查頁(yè)面節(jié)點(diǎn)時(shí),只關(guān)注子節(jié)點(diǎn)中的第一個(gè),如果當(dāng)前節(jié)點(diǎn)的第一個(gè)節(jié)點(diǎn)不是頁(yè)面節(jié)點(diǎn),繼續(xù)在孫子節(jié)點(diǎn)的第一個(gè),直到節(jié)點(diǎn)類型為頁(yè)面節(jié)點(diǎn)為止;
調(diào)度引擎
指的是在基座中,對(duì)權(quán)限樹數(shù)據(jù)處理,接收基座業(yè)務(wù)層調(diào)度,通過(guò)對(duì)權(quán)限樹遍歷、查詢操作對(duì)業(yè)務(wù)層輸出運(yùn)算結(jié)果的一個(gè)抽象分層;
2 整體流程圖
如圖3 為基座技術(shù)方案的整體流程圖,下面詳細(xì)介紹
圖 3
3 技術(shù)方案-分步描述
第 1 步,配置權(quán)限菜單樹,關(guān)聯(lián)子項(xiàng)目頁(yè)面
?如圖 3 所示,首先會(huì)在權(quán)限中心配置權(quán)限菜單樹,權(quán)限菜單樹有很多級(jí),比如一級(jí)平臺(tái),二級(jí)平臺(tái),二級(jí)平臺(tái)一級(jí)菜單,二級(jí)菜單等,三級(jí)菜單等;
?然后需要在菜單樹上,掛在子項(xiàng)目的頁(yè)面,子項(xiàng)目頁(yè)面的一些權(quán)限按鈕等;
?最后權(quán)限中心對(duì)不同的用戶賦予權(quán)限菜單樹的子集;
第 2 步,基座菜單樹數(shù)據(jù)處理;
?基座從接口拿到權(quán)限菜單樹關(guān)聯(lián)頁(yè)面數(shù)據(jù)后,觸發(fā)調(diào)度引擎初始化、并把基座的業(yè)務(wù)更新函數(shù)傳給調(diào)度引擎;
?基座獲取到菜單樹以后,廣度優(yōu)先算法(如圖4)遍歷整個(gè)樹結(jié)構(gòu),并建立節(jié)點(diǎn)間父子關(guān)聯(lián)關(guān)系。根據(jù)節(jié)點(diǎn)是否掛載了子項(xiàng)目頁(yè)面定義前端節(jié)點(diǎn)類型 ;最終形成圖 5 的數(shù)據(jù);
?在樹遍歷過(guò)程中,會(huì)統(tǒng)一在基座左側(cè)邊欄這一層級(jí)的節(jié)點(diǎn),做一個(gè)門戶節(jié)點(diǎn)的標(biāo)記;
圖4
?如圖 5 所示,前端調(diào)度關(guān)系中,把權(quán)限菜單樹分成 兩種節(jié)點(diǎn)類型,掛載了子項(xiàng)目頁(yè)面的節(jié)點(diǎn),定義為頁(yè)面節(jié)點(diǎn)。另一種沒(méi)掛載子項(xiàng)目頁(yè)面的節(jié)點(diǎn),定義為分組節(jié)點(diǎn);在基座調(diào)度中,不關(guān)心權(quán)限按鈕節(jié)點(diǎn),在數(shù)據(jù)處理中,頁(yè)面節(jié)點(diǎn)就是整個(gè)權(quán)限菜單樹的葉子節(jié)點(diǎn);
圖 5
第 3 步,點(diǎn)擊基座邊欄或公共子菜單
?點(diǎn)擊基座邊欄,觸發(fā)點(diǎn)擊菜單流程,點(diǎn)擊的節(jié)點(diǎn)即為 “觸發(fā)節(jié)點(diǎn)”;
?由觸發(fā)節(jié)點(diǎn),向第一子系節(jié)點(diǎn)查頁(yè)面節(jié)點(diǎn),查頁(yè)面節(jié)點(diǎn)同時(shí),會(huì)同時(shí)匹配門戶節(jié)點(diǎn);
?沒(méi)有匹配到門戶節(jié)點(diǎn),由觸發(fā)節(jié)點(diǎn)向父系節(jié)點(diǎn)匹配門戶節(jié)點(diǎn);
第 4 步,刷新頁(yè)面或跨子項(xiàng)目跳轉(zhuǎn)
?這時(shí)沒(méi)有直接的觸發(fā)節(jié)點(diǎn),只能通過(guò) url 上的標(biāo)識(shí)和菜單樹上的頁(yè)面節(jié)點(diǎn)進(jìn)行匹配
?匹配到頁(yè)面節(jié)點(diǎn)以后,由頁(yè)面節(jié)點(diǎn)向父系節(jié)點(diǎn)查門戶節(jié)點(diǎn);
第 5 步,通過(guò)門戶節(jié)點(diǎn)輸出 “運(yùn)算結(jié)果”
?這里的運(yùn)算結(jié)果主要包含:基座左側(cè)邊欄、公共子菜單菜單樹列表、菜單高亮、產(chǎn)品平臺(tái)名稱等一系列基座需要正確顯示所需要的數(shù)據(jù);
?基座調(diào)度引擎,根據(jù)觸發(fā)調(diào)度的類型;如果是點(diǎn)擊菜單觸發(fā)會(huì)執(zhí)行 切換頁(yè)面操作,刷新和跨子項(xiàng)目跳轉(zhuǎn)則不需要觸發(fā);
第 6 步,基座業(yè)務(wù)層執(zhí)行運(yùn)算結(jié)果;
?基座調(diào)度引擎,在接收基座業(yè)務(wù)層調(diào)度指令后,通過(guò)對(duì)圖 5 權(quán)限樹的遍歷、查詢操作,最終獲取運(yùn)算結(jié)果;
? 調(diào)用基座業(yè)務(wù)層的更新函數(shù),執(zhí)行運(yùn)算結(jié)果,到此基座調(diào)度流程結(jié)束;
四,基座-子項(xiàng)目結(jié)構(gòu)通信圖
1 基座分為業(yè)務(wù)層、調(diào)度引擎兩部分;
?基座業(yè)務(wù)層主要包含:左側(cè)邊欄、頂部邊欄、頂級(jí)平臺(tái)菜單等 UI顯示;
?基座調(diào)度層,以 qiankun 微前端為基礎(chǔ),豐富擴(kuò)展了權(quán)限樹數(shù)據(jù)處理、調(diào)度運(yùn)算、雙向通信、子項(xiàng)目分組分步預(yù)加載策略等;
2 基座子項(xiàng)目通信和路由調(diào)度
?基座在執(zhí)行主題切換時(shí)候,通過(guò) qiankun 的 setGlobalState 通知子項(xiàng)目;子項(xiàng)目在綁定項(xiàng)目空間等特定需求時(shí),通過(guò)調(diào)度引擎封裝的 eventCenter 和基座反向通信;
?跨子項(xiàng)目跳轉(zhuǎn),子項(xiàng)目會(huì)自行觸發(fā) pushState,點(diǎn)擊基座菜單跳轉(zhuǎn)流程,由調(diào)度引擎觸發(fā) pushState 觸達(dá)
? qiankun 的 pushSate 劫持策略,觸發(fā) popstate 子項(xiàng)目頁(yè)面因此觸發(fā)更新;
圖 6
3 路由劫持策略原理
基座調(diào)度引擎通過(guò)監(jiān)聽 popstate 來(lái)獲取刷新、跨子項(xiàng)目跳轉(zhuǎn)的指令,從而觸發(fā)調(diào)度流程;以下為原理解析:
?以 Vue 為例,Vue 通過(guò) 主動(dòng)觸發(fā) pushState、replaceState 或者監(jiān)聽 popstate 變化觸發(fā)頁(yè)面發(fā)生變化;
?但是跨子項(xiàng)目跳轉(zhuǎn),執(zhí)行 pushState 并沒(méi)有觸發(fā) popState 基座調(diào)度引擎又怎么能監(jiān)聽到呢?
?通過(guò)閱讀 qiankun 中依賴的庫(kù) single-spa 的源碼,navigation 模塊劫持了 pushstate 方法,只要觸發(fā) pushstate,就會(huì)觸發(fā) popstate 事件,關(guān)鍵代碼如下所示:
function patchedUpdateState(updateState, methodName) {
return function () {
const urlBefore = window.location.href;
const result = updateState.apply(this, arguments);
const urlAfter = window.location.href;
if (!urlRerouteOnly || urlBefore !== urlAfter) {
if (isStarted()) {
window.dispatchEvent(
createPopStateEvent(window.history.state, methodName)
);
} else {
reroute([]);
}
}
return result;
};
}
window.history.pushState = patchedUpdateState(
window.history.pushState,
"pushState"
);
五,基于 qiankun 功能擴(kuò)展:
1 沙箱隔離
js 沙箱利用 qiankun 沙箱機(jī)制;
css 沙箱,qiankun 的 css 沙箱不健全;我們接入的又是老項(xiàng)目,目前的策略是 :
?基座通過(guò)特殊命名空間 .susceptor、element-ui 通過(guò) .spr 跟子項(xiàng)目隔離;
?子項(xiàng)目通過(guò)特殊命名空間,例如 .datacenter-xxx 跟基座隔離
?子項(xiàng)目對(duì) .el- 不執(zhí)行隔離,目的是為了統(tǒng)一控制布局、主題,同時(shí)做性能優(yōu)化;
2 通信機(jī)制
基座和子項(xiàng)目,屬于典型的主從結(jié)構(gòu),采用單向數(shù)據(jù)流通信;為了滿足特殊場(chǎng)景子項(xiàng)目跟基座通信需求,在 qiankun 的通信基礎(chǔ)上,封裝了 eventCenter 僅用于 子項(xiàng)目 跟基座通信;
?基座 -> 子項(xiàng)目,通過(guò)qiankun 的 onGlobalStateChange 通信
?子項(xiàng)目 -> 基座,通過(guò) subActions 封裝的 eventCenter 通信
3 分組、分步、動(dòng)態(tài)預(yù)加載機(jī)制
目前標(biāo)品基座已經(jīng)加載了 30多個(gè)前端子項(xiàng)目,這么多前端子項(xiàng)目在首個(gè)子項(xiàng)目掛載后執(zhí)行預(yù)加載,有可能會(huì)阻塞正常頁(yè)面訪問(wèn);
分組指的是,通過(guò)產(chǎn)品劃定的平臺(tái),只有切換到當(dāng)前平臺(tái)才會(huì)加載到當(dāng)前平臺(tái)的子項(xiàng)目;
分步指的是,當(dāng)前平臺(tái)子項(xiàng)目過(guò)多時(shí),一次預(yù)加載少量子項(xiàng)目,分多次預(yù)加載;
動(dòng)態(tài)指的是,基座在首個(gè)子項(xiàng)目掛載后,會(huì)檢測(cè)網(wǎng)速,只有網(wǎng)速良好時(shí)才會(huì)執(zhí)行預(yù)加載;
4 跨子項(xiàng)目跳轉(zhuǎn)
跨子項(xiàng)目跳轉(zhuǎn)的背景在于 BDP 一站式開發(fā)與管理平臺(tái),是一個(gè)大產(chǎn)品,數(shù)據(jù)規(guī)劃、數(shù)據(jù)集成 等是其中的模塊;模塊之間存在一些跳轉(zhuǎn),對(duì)于前端就是跨子項(xiàng)目跳轉(zhuǎn);
我們目前的跨子項(xiàng)目跳轉(zhuǎn),由 subActions 封裝,抹平了標(biāo)品基座 和 數(shù)據(jù)中臺(tái)融合容器的區(qū)別;
基座跳轉(zhuǎn)邏輯如下:
crossAppJump: ({ subApp='', path= '', paramsStr='', target= ''})=> {
let jumpUrl = `/susceptor/${subApp}${path}`
if(paramsStr){
jumpUrl = `${jumpUrl}${paramsStr}`
}
if(target === '_blank'){
window.open(`${location.origin}${jumpUrl}`)
} else {
window.history.pushState({ portalPushState: true }, null, jumpUrl);
}
},
數(shù)據(jù)中臺(tái)融合跳轉(zhuǎn)邏輯如下:
crossAppJump: ({ subApp='', path= '', paramsStr='', target= ''})=> {
let jumpUrl = `/${subApp}${path}`
window.__datafuse_jssdk__.crossAppJumpFnGetter({
path: jumpUrl,
paramsStr,
target
})();
}
5,接口訪問(wèn)及登錄鑒權(quán);
由于微前端的技術(shù)形態(tài),子項(xiàng)目在基座中加載實(shí)質(zhì)是基座容器的 一段 html,所有接口均是以基座的 域名進(jìn)行接口轉(zhuǎn)發(fā);由因?yàn)閿?shù)據(jù)中臺(tái)融合,所以子項(xiàng)目請(qǐng)求后端接口均是以 /api/datacenter/項(xiàng)目名 開頭;
通過(guò)以下 nginx 示例,我們把基座、子項(xiàng)目 的頁(yè)面訪問(wèn)、接口訪問(wèn)鏈路 說(shuō)明白;
# proxy.conf 示例(已做脫敏處理,均不是項(xiàng)目升級(jí)名稱)
server {
listen 80;
server_name unify.external.dadacenter;
charset utf-8;
location /sub-app{
alias /export/web/sub-app-web;
try_files $uri $uri/ /index.html =404;
}
location /api/datacenter/subapp {
proxy_pass http://server-sup-app/;
}
location /susceptor{
alias /export/web/susceptor; on;
try_files $uri $uri/ /index.html =404;
}
location /api{
proxy_pass http://sever-api/;
}
}
# server.conf 示例(已做脫敏處理,均不是實(shí)際項(xiàng)目)
upstream server-sup-app{
server 111.112.113.114:10001;
}
upstream sever-api{
server 111.112.113.114:1000;
}
鑒權(quán),前端不需要開發(fā),但是需要知道,后端是通過(guò)頂級(jí)域名種 cookie 鑒權(quán)的;例如:unify.external.bigdata 測(cè)試環(huán)境是在 .external.bigdatao 域名下,這也是為什么本地開發(fā)需要配置 host:127.0.01 loca.external.dadacenter ;
六,寫在最后,不忘初心
本節(jié)提供兩個(gè)圖, 對(duì)上文介紹的微前端實(shí)踐,有進(jìn)一步的能力提升,有感興趣的同學(xué)歡迎一起討論;
1,整體流程圖;
如圖 6 所示,配置流程:配置權(quán)限菜單樹,然后配置子項(xiàng)目-子項(xiàng)目頁(yè)面兩級(jí);最后把權(quán)限菜單樹 和 子項(xiàng)目-子項(xiàng)目頁(yè)面關(guān)聯(lián)起來(lái),形成如圖 7 的權(quán)限樹-子項(xiàng)目關(guān)聯(lián)數(shù)據(jù)模型;
基座兩個(gè)調(diào)度流程,跟上文類似,但是多了子項(xiàng)目維度,基座在加載子項(xiàng)目的時(shí)候,就可以把子項(xiàng)目 在權(quán)限樹的 權(quán)限按鈕信息全部給到子項(xiàng)目;
圖 6
2 權(quán)限菜單樹模型;
上文介紹的基座調(diào)度流程是簡(jiǎn)化后的版本,項(xiàng)目節(jié)點(diǎn)只有 分組節(jié)點(diǎn)、頁(yè)面節(jié)點(diǎn);但是從能力層缺失了 子項(xiàng)目維度;在設(shè)計(jì)之初,如圖 7 所示:項(xiàng)目節(jié)點(diǎn)包含了 分組節(jié)點(diǎn)、子項(xiàng)目節(jié)點(diǎn)、子項(xiàng)目頁(yè)面節(jié)點(diǎn)、頁(yè)面節(jié)點(diǎn) 4種節(jié)點(diǎn)類型;文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-420192.html
圖 7文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-420192.html
到了這里,關(guān)于【數(shù)據(jù)中臺(tái)商業(yè)化】數(shù)據(jù)中臺(tái)微前端實(shí)踐的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!