导入第三方组件_大型 web 应用公共组件架构是如何来的?
來(lái)源:騰訊AlloyTeam
https://mp.weixin.qq.com/s/gVUJRF_nLHOT_iXDXQ8F-w
騰訊文檔公共組件歷史包袱
1. 架構(gòu)問題——開發(fā)層面
騰訊文檔管理的公共組件, 設(shè)計(jì)之初,采用了各種便于快速迭代的設(shè)計(jì)方式,組件代碼結(jié)構(gòu)和規(guī)范也缺乏統(tǒng)一,在長(zhǎng)期的開發(fā)過程中質(zhì)量沒有得到保障。隨著需求不斷累積,目前存在比較大的歷史包袱。大量組件錯(cuò)綜復(fù)雜,相互輯合緊密,而導(dǎo)致不管多么小的改動(dòng)都需要數(shù)天的惡戰(zhàn)才能完,對(duì)于開發(fā)新功能和修復(fù)缺陷的同時(shí)時(shí),都異常痛苦。主要存在的問題是以下幾點(diǎn)。
1.1 難以預(yù)料第三方公共組件導(dǎo)致的卡頓
騰訊文檔管理的公共組件(以下稱FC)主要通過 script-loader 動(dòng)態(tài)加載承載了各個(gè)頁(yè)面的公共業(yè)務(wù)邏輯,然后將腳本注入到品類的 HTML 中,比如登陸、分享,權(quán)限等。這些邏輯都是同一個(gè)線程中執(zhí)行的。
第三方組件是由不同團(tuán)隊(duì)和開發(fā)人員在維護(hù)著,往往有著不可控制的預(yù)期,品類方難以保證引入某一個(gè)組件的性能是否合理,從而容易導(dǎo)致品類編輯發(fā)生卡頓,及性能數(shù)據(jù)下降。
目前在 excel 調(diào)用公共組件過程中,會(huì)手動(dòng)停止卡頓監(jiān)控,從而讓公共組件邏輯不影響詳情頁(yè)的卡頓數(shù)據(jù)。然而,這無(wú)法從根本上改變用戶主進(jìn)程卡頓的體驗(yàn)問題。
// 以下偽代碼async loadModule(name){ ? ?// 卡頓監(jiān)控停止 ? ?jank.stopReport(); ? ?await dosomeThingToLoadModule(name); ? ?jank.restartReport();}
【案例】 打開權(quán)限組件 cpu 暴漲,表格卡頓。
1.2 script-loader 加載形式鏈路非常長(zhǎng),公共組價(jià)加載異常延遲。
首先需要加載 assets.json 依賴映射文件,然后再異步加載需要功能的 js 代碼,最后再初始化組件,向后臺(tái)請(qǐng)求組件所需數(shù)據(jù),進(jìn)行渲染,最終才能完整展示。這是一個(gè)非常長(zhǎng)的鏈路,導(dǎo)致用戶使用體驗(yàn)相關(guān)功能非常耗時(shí)。
1.3 發(fā)布沒有版本控制。
“一次更新,多端升級(jí)” 本來(lái)是 FC 設(shè)計(jì)之初的一種考慮,但在日積月累的迭代中,我們積累了無(wú)數(shù) bug,每一次常規(guī)發(fā)布之夜都伴隨著驚恐與噩夢(mèng)。由于模塊A 發(fā)布修復(fù)了某個(gè) ppt 的 bug,帶了某個(gè) word 的新 bug; 由于某一個(gè)版本的升級(jí),帶來(lái)全品類功能的崩潰。缺乏版本控制的后果就是,為了節(jié)省半個(gè)小時(shí)的包升級(jí)時(shí)間,帶來(lái)了大量調(diào)用品類方之間的缺陷連鎖反應(yīng)。我們的設(shè)計(jì)目標(biāo)除了盡可能保證發(fā)布效率,發(fā)布的質(zhì)量和穩(wěn)定性也是非常重要的。
1.4 組件調(diào)用形式不規(guī)范和統(tǒng)一
// 以下偽代碼 // 業(yè)務(wù)A const someModule = await loadModule('someModule'); someModule.init({ ? ?xxx: 'yyyy', ? ?zzz: 'hello', ? ?from: 'xxx' })
// 以下偽代碼// 業(yè)務(wù)B const someModule = await loadModule('someModule'); someModule.init({ ? ?bbb: 'yyyy', ? ?ccc: 'hello' })
公共組件沒有統(tǒng)一的入?yún)⒁?guī)范。每次開發(fā)的步驟是,在品類 A 已經(jīng)提前接入前提某組件下,品類 B、C直接復(fù)制黏貼過去,然后完事。由此帶來(lái)的問題是:我們發(fā)現(xiàn)大量由于品類直接差異性導(dǎo)致的公共組件 bug 。
1.5 通信機(jī)制混亂
script-loader 即承擔(dān)了模塊加載的職責(zé),內(nèi)部有又事件通信的邏輯。而公共組件和各個(gè)品類的通信除了使用SLR.listen 外,同時(shí)又摻雜 window.addEventListener,導(dǎo)致很多地方重復(fù)監(jiān)聽,同時(shí)在定位問題時(shí)帶來(lái)了困擾。示例:excel 和 word 對(duì)應(yīng)的通信不一樣。
// 偽代碼window.something.listen('someEvent', ()=>{})
// 偽代碼document.addEventlistener.listen('someEvent', ()=>{})
1.6 內(nèi)部大量使用全局變量
FC 倉(cāng)庫(kù)僅 xxx 這個(gè)變量就有500 多處調(diào)用方。公共組件使用全局變量容易會(huì)造成對(duì)詳情頁(yè)的污染,同時(shí)讓組件邏輯與品類的特定變量耦合,一旦某一個(gè)品類對(duì)應(yīng)的字段在迭代中發(fā)生變化,就會(huì)造成意外 bug 。
2. 架構(gòu)問題——產(chǎn)品層面
架構(gòu)的不合理設(shè)計(jì),會(huì)帶來(lái)一些很大的負(fù)面影響,尤其是在需求的開發(fā)周期上。這本身是一個(gè)惡性循環(huán):
解決方案思考
綜上所述,我們可以發(fā)現(xiàn),目前我們?cè)瓉?lái)對(duì)第三方公用組件的設(shè)計(jì)思路是——把公用組件當(dāng)作編輯頁(yè)不可或缺的耦合部分。實(shí)際上,公共組件,例如,權(quán)限,分享,通知等功能,具備獨(dú)立應(yīng)用的功能,它們應(yīng)該更像是一個(gè)可拔插的插件,品類不應(yīng)該關(guān)心插件的內(nèi)部細(xì)節(jié),插件也不應(yīng)該有權(quán)限影響和破壞外部主進(jìn)程。讓每次變更都變得可控,并且避免缺陷,同時(shí)最大程度地滿足功能性和靈活性的要求是這次架構(gòu)設(shè)計(jì)的目標(biāo)。
解決方案是建設(shè)可拔插式插件化公共組件體系。定制標(biāo)準(zhǔn)的插件化規(guī)范,可便于拓展成對(duì)第三方開發(fā)者開發(fā)插件的體系。而 FC 公共組件是作為官方內(nèi)置插件的形式存在。插件體系有幾個(gè)比較關(guān)鍵的點(diǎn):第一是,第三方插件質(zhì)量會(huì)參差不齊,如何約束插件的運(yùn)行不會(huì)導(dǎo)致頁(yè)面的卡頓。第二點(diǎn)是,插件如何調(diào)用文檔SDK,也即使如何規(guī)范插件和主線程的通信問題。第三點(diǎn)是,插件安裝,卸載等后臺(tái)管理服務(wù)。
2.1 如何安全的運(yùn)行插件?
2.1.1 插件類型
首先我們的插件體系分為兩類:純計(jì)算邏輯型插件 和 UI 交互式插件。純計(jì)算邏輯插件,比如一個(gè)自定義函數(shù),一個(gè)自定義任務(wù)等。這種插件可以通過使用 web worker 進(jìn)行多線程計(jì)算進(jìn)行隔離。UI 交互式插件,比如分享彈窗,權(quán)限側(cè)邊欄等,目前 FC 公共組件全部是這種類型。這種插件需要復(fù)雜的 UI 交互,我們可以通過 chrome 的 site-isolation 特性(參考第三方 web 應(yīng)用進(jìn)程隔離),用不同域的域名動(dòng)態(tài)創(chuàng)建 iframe,對(duì)應(yīng)的 iframe 內(nèi)容區(qū)域會(huì)和主進(jìn)程進(jìn)行隔離,從而保證品類的性能和安全性。
2.2 插件如何與主進(jìn)程通信
出于安全限制,插件不應(yīng)該直接訪問和寫入主進(jìn)程任何數(shù)據(jù)。需要建立一套 rpc 通信協(xié)議打通插件和主進(jìn)程的調(diào)用。
2.2.1主進(jìn)程接口安全暴露
excel 通過 di 依賴服務(wù)化后,各種依賴將會(huì)以服務(wù)化的形式對(duì)外提供。對(duì)外暴露 api 接口,提供給內(nèi)部和外部調(diào)用。
2.2.2插件接口安全暴露
基于安全性考慮,插件只能調(diào)用平臺(tái)方提供的安全接口,這些接口可以 api 服務(wù)化的形式對(duì)外暴露。在初始化的過程注入到一個(gè) API 服務(wù)工廠中返回給一個(gè)緩存對(duì)象,提供給插件使用。這些對(duì)象如何暴露給插件?這里我們參考 vscode 機(jī)制,可以攔截 require 接口,將緩存的插件api 注入到插件上下文。
2.2.3 插件進(jìn)程 api 和 主進(jìn)程 api 通信
定義標(biāo)準(zhǔn)的 worker/iframe 進(jìn)程與主進(jìn)程通信機(jī)制。參照 vscode 我們可以巧用 proxy 代理(IE 11 不兼容),在插件調(diào)用 api 時(shí)進(jìn)行攔截,統(tǒng)一轉(zhuǎn)換成 message send 調(diào)用,可以避免每次api 調(diào)用手動(dòng)觸發(fā) message 通信,簡(jiǎn)化調(diào)用流程。
2.3 插件 UI 擴(kuò)展點(diǎn)
騰訊文檔公共組件交互上只有兩種組成,分別是 dialog 彈窗和 slidebar 側(cè)邊欄,dialog 彈窗代表是添加文件夾面包、分享面板、vip 支付面板等。側(cè)邊欄有權(quán)限、通知列表等。這兩種類型組件,我們分別為插件 UI 展示提供統(tǒng)一的面板。插件編寫時(shí)需要配置指定類型,調(diào)用時(shí)在特定區(qū)域承載視圖。
2.4 插件管理體系
2.4.1 部署
用戶開發(fā)的插件需要有管理平臺(tái),按照規(guī)范開發(fā)完后,發(fā)布到插件管理服務(wù)。管理服務(wù)具備生成插件描述信息,部署到靜態(tài)資源,為 UI 組件形態(tài)的插件動(dòng)態(tài)生成插件三級(jí)域名。
2.4.鑒權(quán)、安裝
用戶授權(quán)給插件,然后才能完成安裝。可訪問權(quán)限比如用戶基本信息,表格信息,確認(rèn)許可后,用戶信息下綁定應(yīng)對(duì)插件。
2.5 調(diào)試
內(nèi)部插件暫時(shí)可以直接代理 sheet 本地進(jìn)行開發(fā)。對(duì)外部插件需要提供一種標(biāo)準(zhǔn)便捷的調(diào)試方式。可選方案有兩種,第一種是通過騰訊文檔調(diào)試工具 Chrome 插件,支持用戶安裝臨時(shí)的本地插件,進(jìn)行開發(fā)。
另外一種是用戶申請(qǐng)調(diào)試開發(fā)權(quán)限,文檔菜單選項(xiàng)內(nèi)增加插件導(dǎo)入,然后上傳到一個(gè)臨時(shí)的調(diào)試服務(wù)服務(wù) ,調(diào)試好后,再進(jìn)行發(fā)布。?
2.5 如何兼容多品類
公用組件插件化依賴品類有相同的服務(wù)化機(jī)制。但各個(gè)品類因?yàn)榇a并不統(tǒng)一,插件化如何兼容各個(gè)品類呢?
有兩種主要方法,第一種是公共組件按照 Excel 服務(wù)化進(jìn)行插件化先行改造,內(nèi)部再暴露全局變量給其他未改造的品類按照原 FC 調(diào)用。
另外一種是將插件化體系進(jìn)行單獨(dú)的 SDK 化,SDK 內(nèi)部做統(tǒng)一的插件化環(huán)境及初始化流程,在各個(gè)品類再進(jìn)行引入。
結(jié)
任何架構(gòu)設(shè)計(jì)都是歷史下的產(chǎn)物,脫離實(shí)際情況談最優(yōu)解都是不切實(shí)際的想法,如何在有限的人力資源和更優(yōu)的方案中取得平衡是一門學(xué)問。一個(gè)模式的提出必定面對(duì)解決一個(gè)問題,隨著時(shí)間的推移,需求不斷調(diào)整和迭代之下,原先的軟件設(shè)計(jì)必定會(huì)變得越來(lái)越脆弱,最終面臨自然崩塌,需要重構(gòu)。但就像一棟房子,工程師設(shè)計(jì)出結(jié)構(gòu)穩(wěn)定和考慮長(zhǎng)遠(yuǎn)的方案(架構(gòu)和可擴(kuò)展性),同時(shí)施工隊(duì)不偷工減料(代碼質(zhì)量),那么房子也會(huì)保值更久,也能更好的面對(duì)新工程的不斷改造。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ----------? END? ----------
推薦閱讀
??2020最新java學(xué)習(xí)路線圖!
??Java ?9 月程序員工資出爐,你拖后腿了嗎?
??CTO 寫的代碼為什么那么強(qiáng)!
?一個(gè)900行代碼得類,你累不累?
如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝下次見(。・ω・。)ノ?總結(jié)
以上是生活随笔為你收集整理的导入第三方组件_大型 web 应用公共组件架构是如何来的?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安装python的moviepy_Mov
- 下一篇: 箱式图 分组_小白学R(三):重复测量数