Shopee在React?Native?架構(gòu)方面的探索及發(fā)展歷程
1. 背景
React Native(下文簡稱 RN)是混合應(yīng)用領(lǐng)域流行的跨端開發(fā)框架。RN 非常適合靈活多變的電商領(lǐng)域業(yè)務(wù),由于 RN 是基于客戶端渲染的技術(shù),所以相較于 H5 頁面,它在用戶體驗方面有一定優(yōu)勢。
伴隨著 Shopee 業(yè)務(wù)的飛速發(fā)展,我們 App 中的 RN 代碼量增長得非常快,出現(xiàn)了構(gòu)建產(chǎn)物體積過大、部署時間太長、不同團(tuán)隊依賴沖突等問題。為了應(yīng)對這些痛點,我們探索了去中心化的 RN 架構(gòu),并結(jié)合該模型自研了系統(tǒng)(Code Push Platform,簡稱 CPP)和客戶端 SDK,覆蓋了多團(tuán)隊的開發(fā)、構(gòu)建、發(fā)布、運行等一系列 RN 研發(fā)周期。經(jīng)過近三年的迭代,現(xiàn)已接入多款公司級核心 App。
Shopee 商家服務(wù)前端團(tuán)隊打造了多款商家端應(yīng)用,大部分用戶是商家服務(wù)人員,他們對業(yè)務(wù)系統(tǒng)高可用和問題及時反饋有著很高的要求,從而也推動我們對 React Native 的架構(gòu)有了更高的要求。
本文會從發(fā)展歷史、架構(gòu)模型、系統(tǒng)設(shè)計、遷移方案四個方向逐一介紹我們?nèi)绾我徊讲降貪M足多團(tuán)隊在復(fù)雜業(yè)務(wù)中的開發(fā)需求。
2. 發(fā)展歷程
隨著業(yè)務(wù)高速發(fā)展,我們的 RN bundle 個數(shù)飛速增加,App 個數(shù)也達(dá)到近十個。整個 RN 項目在開發(fā)模型、部署模型和架構(gòu)模型三個維度都發(fā)生了變化,從單團(tuán)隊發(fā)展成多團(tuán)隊,從一個 bundle 發(fā)展成多個 bundle,從中心化架構(gòu)發(fā)展成為去中心化,最終發(fā)展成為每個團(tuán)隊的業(yè)務(wù)代碼可以獨立地開發(fā)、部署、運行。
整個發(fā)展歷史分為 4 個階段,分別是單 bundle 集中開發(fā)模式、單 bundle 多業(yè)務(wù)組開發(fā)模式、多 bundle 中心化發(fā)布模式、多 bundle 去中心化發(fā)布模式。
2.1 第一階段:單 bundle 集中開發(fā)模式
最初的 RN 整體技術(shù)架構(gòu)相對簡單。由于當(dāng)時業(yè)務(wù)形態(tài)不算復(fù)雜,為了滿足獨立團(tuán)隊在同一個代碼倉庫當(dāng)中的開發(fā)流程,整個發(fā)布流程是基于 CDN 的更新發(fā)布,并且使用配置文件記錄 RN bundle 文件的版本以及下載地址,以此進(jìn)行資源管理。整個發(fā)布的產(chǎn)物有兩個,一個是 RN 資源包,一個是用于資源版本管理的 JSON 配置文件。
每次 RN 資源在完成構(gòu)建后,這兩種構(gòu)建產(chǎn)物會被放置在靜態(tài)資源目錄下。App 在特定的時間節(jié)點(例如 App 重啟等)會自動拉取配置文件檢查資源更新狀態(tài),然后再從 CDN 拉取 RN 靜態(tài)資源。在下一次打開頁面的時候,App 會加載最新的頁面內(nèi)容。
隨著業(yè)務(wù)發(fā)展,越來越多業(yè)務(wù)團(tuán)隊期望使用 RN 技術(shù)棧開發(fā)業(yè)務(wù),這種情況讓已有架構(gòu)發(fā)生改變,我們自然地產(chǎn)生了“多個業(yè)務(wù)組多個代碼倉庫”的想法。
2.2 第二階段:單 bundle 多業(yè)務(wù)組開發(fā)模式
針對上述問題,多業(yè)務(wù)組的研發(fā)解決方案是 host-plugin 這種模式。
host 用于管理公共依賴和通用邏輯,它將 React、React Native、Shopee RN SDK 等通過一個獨立的倉庫管理起來,保證了特殊 RN 依賴的“singleton”(單例模式)條件,避免了部分客戶端組件的重疊依賴,而這種重疊依賴是 RN 官方不允許的。
一個 host 對應(yīng)著多個 plugin 倉庫,業(yè)務(wù)代碼倉庫則是被看作為一個插件(plugin),以插件的形式接入主應(yīng)用當(dāng)中。業(yè)務(wù)團(tuán)隊可以按自己的編碼規(guī)范來管理這個倉庫。每個插件倉庫會被視為 host 項目的 npm 依賴,它的構(gòu)建是一個集中發(fā)布的流程。所有代碼都會集成在 host 項目當(dāng)中執(zhí)行構(gòu)建腳本。這種模式滿足超級 App 的要求。
與此同時,host-plugin 的模式也帶來了一個“難題”,業(yè)務(wù)發(fā)展使得 RN 產(chǎn)物體積逐漸變大,過大的產(chǎn)物會影響客戶端的解壓效率和 RN 容器加載 JS 時長。
2.3 第三階段:多 bundle 中心化架構(gòu)模式
針對 RN 產(chǎn)物體積過大的問題,我們利用構(gòu)建工具將打包產(chǎn)物細(xì)分成多個 bundle,這一優(yōu)化是非常有必要的,我們稱它為“分包”。host 項目對應(yīng)的是公共包,plugin 項目對應(yīng)的是業(yè)務(wù)包。
整個構(gòu)建發(fā)生在 host 項目,項目的模式還是“集中構(gòu)建”和“集中發(fā)布”。多 bundle 產(chǎn)物將會發(fā)布到系統(tǒng)當(dāng)中,客戶端將拉取熱更新的內(nèi)容??蛻舳藭葱杓虞d對應(yīng)的 bundle,RN 容器單次加載消耗的資源大大減少,解決了效率問題。
但是它的缺點也很明顯。隨著業(yè)務(wù)團(tuán)隊的變大和業(yè)務(wù)內(nèi)容的擴(kuò)張,多 bundle 中心化發(fā)布模式同樣也具備四個弊端:
針對 RN 的運行時,即使分包技術(shù)使得產(chǎn)物分離,但是它們還是運行在同一個 JSContext 當(dāng)中,這種情況可能會導(dǎo)致依賴沖突和環(huán)境變量污染;在開發(fā)調(diào)試的過程中,項目重依賴于 host 項目,每次存在著代碼變更,需要重新加載很多內(nèi)容,讓開發(fā)調(diào)試不太友好;在項目構(gòu)建的過程中,打包速度受到 plugin 個數(shù)的影響,特大型應(yīng)用甚至需要 50 分鐘執(zhí)行一次構(gòu)建,過長的構(gòu)建耗時嚴(yán)重影響了發(fā)布效率;在部署發(fā)布的過程中,host 項目維護(hù)者負(fù)責(zé)整個 App,每個業(yè)務(wù)組不能獨立發(fā)布,發(fā)布時間會綁定在一起。當(dāng)出現(xiàn) live issue 的情況,開發(fā)者需要花費大量的溝通成本,且只能整體回滾。 2.4 第四階段:多 bundle 去中心化架構(gòu)模式
去中心化 React Native 架構(gòu)模式與網(wǎng)頁的“微前端”或者客戶端的“微應(yīng)用”的概念類似,滿足了多業(yè)務(wù)團(tuán)隊獨立開發(fā)部署,能夠在同一個 App 各模塊獨立運行。它涵蓋了開發(fā)、構(gòu)建、發(fā)布、運行等多個方面。該模型解決了上面所說的四個弊端,并針對整個研發(fā)體系有了全面的升級,優(yōu)點有:RN 運行時的互不干擾,開發(fā)調(diào)試的高效,構(gòu)建發(fā)布的獨立性。
下文會重點介紹項目的去中心化 RN 架構(gòu)和系統(tǒng)設(shè)計,以及我們是怎樣做到靈活性和穩(wěn)定性的平衡的。
3. 去中心的 RN 架構(gòu)模型
簡單來說,去中心化的 RN 發(fā)布模型涉及到四個部分:獨立的 JS 運行時;獨立的開發(fā)流程;獨立的構(gòu)建流程;獨立的發(fā)布流程。在這四個關(guān)鍵環(huán)節(jié)的幫助下,每個團(tuán)隊按自己的節(jié)奏掌控 RN 的研發(fā)流程。
3.1 獨立 JS 運行時
獨立運行時(多 JSContext,執(zhí)行上下文環(huán)境)的出現(xiàn)是去中心化架構(gòu)的最大特色。獨立運行時是對獨立發(fā)布的完美保證,將 RN 運行代碼按照 plugin 維度進(jìn)行隔離,它可以有效避免不同業(yè)務(wù)之間的變量沖突以及依賴沖突問題,即“plugin A”的發(fā)布絕對不會影響到“plugin B”。
它的設(shè)計主要包含以下三點:
提前創(chuàng)建 JSContext 且預(yù)加載公共包;進(jìn)入 plugin 的頁面,SDK 會查看對應(yīng)的 JSContext 是否已被實例化。如果已經(jīng)被實例化,就直接使用,否則從 JSContext Pool 選取一個獨立的上下文,加載執(zhí)行業(yè)務(wù)包,各個 plugin 之間運行時是隔離的;退出業(yè)務(wù)頁面時,該 JSContext 不會立即銷毀,而是放入一個緩存池,使得重復(fù)進(jìn)入該業(yè)務(wù)可以獲得極致體驗。
裝置 JSContext 的容器可以是線程或者進(jìn)程。為了避免它頻繁創(chuàng)建和回收,我們要維護(hù)緩存池且盡可能地復(fù)用現(xiàn)有的 JSContext。
這里我們采用 Least Frequently Recently Used(簡稱 LFRU)的策略。當(dāng)剛退出的應(yīng)用被重新打開,該 JSContext 會被重新啟用。這樣,我們能夠節(jié)省 85% 的首屏渲染時長。緩存?zhèn)€數(shù)管理是可配置的,業(yè)務(wù)方可以根據(jù)應(yīng)用的規(guī)模作為合理的預(yù)估。當(dāng)該 RN 頁面還在使用中,即使超出預(yù)估數(shù),該上下文也不會立即被回收,該設(shè)計有效地保證頁面的可用性。
3.2 開發(fā)流程
上文提及 RN 項目的調(diào)試效率問題,它會隨著業(yè)務(wù)代碼的體量增多,代碼調(diào)試效能也會隨之下降。每個開發(fā)者的效率問題直接影響到大家的“幸福感”。相比之下,RN 去中心化發(fā)布則是針對開發(fā)流程做了特定的優(yōu)化。
隨著獨立運行時環(huán)境的出現(xiàn),RN 進(jìn)入調(diào)試的時候,客戶端可以做到只加載一個 plugin 到對應(yīng)的 JSContext 中,其他 plugin 則采用內(nèi)置 cache。
這樣做有兩個好處:一是保證了服務(wù)啟動范圍的最小化,保證了代碼熱加載的效率;二是確保開發(fā)和構(gòu)建兩種流程的一致性,這樣會讓一些問題在開發(fā)階段提前暴露出來,比如 babel 插件缺失導(dǎo)致的編譯問題。這樣的“去中心化”的開發(fā)流程提高了 RN 調(diào)試效率。
3.3 構(gòu)建流程
隨著業(yè)務(wù)發(fā)展,某 App 的 RN plugin 數(shù)有 4 個,舊構(gòu)建流程受到 plugin 個數(shù)的影響,集中構(gòu)建時長超過 20 分鐘。而采用去中心化 RN 架構(gòu),構(gòu)建時長不再隨 plugin 個數(shù)增長,只和該 plugin 代碼量有關(guān),穩(wěn)定在 5 分鐘左右。
新架構(gòu)也是同樣基于 host-plugin 模型,獨立倉庫的隔離讓每個團(tuán)隊有自由的發(fā)展空間。考慮到在應(yīng)用內(nèi)的基礎(chǔ) Native 依賴是統(tǒng)一的,host 項目僅用來管理統(tǒng)一的公共依賴。項目需要優(yōu)先將 common bundle 構(gòu)建完成,系統(tǒng)會記錄公共包中的依賴信息。當(dāng)每個 plugin 項目進(jìn)行構(gòu)建的時候,構(gòu)建工具會剔除掉公共包依賴信息,并完成業(yè)務(wù)包的構(gòu)建。每個業(yè)務(wù)包的構(gòu)建產(chǎn)物都是獨立地存放于系統(tǒng)當(dāng)中。系統(tǒng)具備獨立回滾、獨立發(fā)布、獨立灰度的能力。
這樣的好處在于構(gòu)建任務(wù)的最小粒度化,每個 plugin 的構(gòu)建不會引起整個項目的重新構(gòu)建,做到真正意義的“按需打包”。
3.4 發(fā)布流程
RN 的構(gòu)建和發(fā)布是兩個獨立的流程。這也意味著 bundle 的構(gòu)建環(huán)節(jié)和發(fā)布環(huán)節(jié)完全解耦,發(fā)布時間點也可以由每個業(yè)務(wù)團(tuán)隊發(fā)布負(fù)責(zé)人靈活安排。每個業(yè)務(wù)組對自己的代碼質(zhì)量負(fù)責(zé),靈活地把控自己的發(fā)版本節(jié)奏,不會影響其他團(tuán)隊線上業(yè)務(wù)。發(fā)布流程里面包含了全量發(fā)布、聯(lián)合發(fā)布、灰度發(fā)布、回滾等操作,后續(xù)章節(jié)會詳細(xì)介紹如何保證發(fā)布的穩(wěn)定性。
4. 系統(tǒng)設(shè)計
對于復(fù)雜的大型項目來說,簡單的熱更新流程已無法滿足多業(yè)務(wù)組協(xié)同合作,我們需要一個功能完善、性能優(yōu)越、操作友好的熱更新系統(tǒng)來滿足復(fù)雜業(yè)務(wù)的發(fā)展。Code Push Platform 由 Node.js 編寫,搭配系統(tǒng)附屬的命令行工具和客戶端 SDK。
為了滿足該系統(tǒng)在多業(yè)務(wù)團(tuán)隊的運作,整個系統(tǒng)從功能角度可以劃分為三大部分,分別是:
多團(tuán)隊權(quán)限管控;bundle 生命周期管理;系統(tǒng)效能提升。
其中,系統(tǒng)效能提升功能又細(xì)分為:
增量差分;多場景入口體積優(yōu)化;一站式多環(huán)境整合。 4.1 多團(tuán)隊權(quán)限管控
系統(tǒng)除了記錄每次構(gòu)建操作,更重要的是工作流程的去中心化,每個 plugin 的權(quán)限是隔離的。每個負(fù)責(zé)人只能在系統(tǒng)內(nèi)部操作,plugin 1 的負(fù)責(zé)人只能觸發(fā)相關(guān)的構(gòu)建和發(fā)布,沒法看到 plugin 2 的操作情況。系統(tǒng)通過嚴(yán)格的權(quán)限管控來規(guī)范所有發(fā)布流程,保證了項目的可控性。
React Native 去中心化發(fā)布的設(shè)計目標(biāo)是節(jié)省不同團(tuán)隊之間的溝通成本。系統(tǒng)會限制他們的構(gòu)建和發(fā)布的動作,各自的發(fā)布不會互相干擾。
權(quán)限的管理呈樹狀結(jié)構(gòu),一個 App 對應(yīng)著一個項目,項目負(fù)責(zé)人默認(rèn)是 App 團(tuán)隊的項目負(fù)責(zé)人。創(chuàng)建一個全新的插件等系統(tǒng)操作需要項目負(fù)責(zé)人審批。一個 App 包含有多個 plugin,每個 plugin 負(fù)責(zé)人默認(rèn)是相應(yīng)的業(yè)務(wù)團(tuán)隊負(fù)責(zé)人,他有權(quán)限分配發(fā)布和構(gòu)建的權(quán)限。
4.2 bundle 生命周期管理
4.2.1 客戶端版本控制
RN 有別于網(wǎng)頁應(yīng)用,它對客戶端有著緊密的依賴關(guān)系。在客戶端底層依賴沒有變化的情況下,一般情況下開發(fā)者可以通過熱更新進(jìn)行 RN 代碼的更新。但是遇到重大的更新,例如 React Native 的版本從 59 升級到 63,不僅僅需要 JavaScript 側(cè)改動,客戶端也要升級版本且沒法繼續(xù)向下兼容。從技術(shù)層面看,它是難以避免的。這種客戶端無法向下兼容的情況,被稱為“斷層”。
系統(tǒng)會提供客戶端版本控制的能力。當(dāng)重大變更出現(xiàn)時,App 負(fù)責(zé)人應(yīng)該在系統(tǒng)上新建一個“斷層信息”,版本號的范圍是從最低 App 兼容版本到最高 App 版本。在這個區(qū)間客戶端才能拉取到該斷層的最新 RN 資源。
如下表所示,大于等于 2.5.0 版本的 App 拉取的是 105 版本 RN 包;在 2.0.0 至 2.5.0 版本拉取到 103 版本 RN 包;在 1.0.0 至 2.0.0 版本拉取到 100 版本 RN 包。
這種措施能夠有效避免潛在風(fēng)險。而最新的需求只會在最新斷層上線,舊的斷層只做線上問題修復(fù)。畢竟是兩套代碼,代碼的維護(hù)有成本,隨著用戶更新至最新版本,應(yīng)當(dāng)逐漸淘汰掉舊斷層。
4.2.2 灰度和回滾
發(fā)布流程里面包含了全量發(fā)布、灰度發(fā)布、回滾等操作。對于大型需求,全量上線會帶來潛在風(fēng)險。一般來說,優(yōu)先針對部分用戶投放新版本,發(fā)布負(fù)責(zé)人可以根據(jù)指定用戶和特定范圍進(jìn)行灰度發(fā)布,逐步擴(kuò)大灰度發(fā)布范圍,直至轉(zhuǎn)到全量。當(dāng)發(fā)現(xiàn)重大 bug 的時候,發(fā)布者可以采用“零構(gòu)建”的方式進(jìn)行“秒級”回滾。
去中心化 RN 架構(gòu)支持每個 plugin 獨立發(fā)布、獨立灰度、獨立回滾,以最小顆粒度的操作來保證質(zhì)量規(guī)避風(fēng)險。plugin 維度級別的灰度和回滾能夠為不同的業(yè)務(wù)團(tuán)隊帶了靈活性,每個業(yè)務(wù)團(tuán)隊可以自行發(fā)布版本,控制灰度節(jié)奏,處理線上問題。
4.3 系統(tǒng)效能提升
4.3.1 差分增量
App 頻繁更新 RN 資源包會造成對用戶流量的消耗,最有效的方式是利用增量更新來節(jié)省流量。RN 資源包涵蓋了編譯后的 JavaScript 產(chǎn)物、圖片、翻譯文件等靜態(tài)資源。它們的前后版本差異即是該版本變更的代碼或者其他資源文件。為了讓差分粒度深入到資源包內(nèi)部,系統(tǒng)專門提供獨立的“差分服務(wù)”,采用二進(jìn)制差分的方式對構(gòu)建產(chǎn)物進(jìn)行差分。
RN 資源包的 diff(差分)操作在服務(wù)端完成 ,patch(整合)操作在 App 端完成。在去中心化 RN 架構(gòu)中,每個 plugin 的差分都是獨立的。plugin 的發(fā)布會自動觸發(fā)差分的執(zhí)行,系統(tǒng)會以 plugin 為維度拉取最近五個版本,Diff Server 則會依次將它們和當(dāng)前版本進(jìn)行差分計算。如果計算成功,會將差分結(jié)果上傳到 CDN 并反饋給系統(tǒng),否則繼續(xù)重試。整個差分操作是一個異步的過程,即使出現(xiàn)“差分服務(wù)”下線等極端情況,系統(tǒng)會自動降級為全量包,保證系統(tǒng)的可用性。
4.3.2 多場景入口體積優(yōu)化
由于 React Native 的構(gòu)建官方依賴于 metro.js,而它并沒有具備無用代碼剔除(tree-shaking)的能力。隨著業(yè)務(wù)代碼的膨脹,包體積的優(yōu)化是一個很重要的問題。
例如,ShopeePay 為公司多款核心 App 提供支付業(yè)務(wù)。ShopeePay plugin 在不同地區(qū)、不同 App 之間存在一些頁面級別差異。同一個倉庫含了所有代碼和資源,但是構(gòu)建腳本會將它們都會打包成為一個產(chǎn)物。很明顯,這導(dǎo)致 ShopeePay 的發(fā)布產(chǎn)物包含大量冗余資源,并非最優(yōu),浪費下載流量,同時也影響代碼的執(zhí)行效率。
我們采用自研的多場景插件(babel-plugin-scene),該插件通過注入的環(huán)境變量設(shè)置一個場景值,babel 可以根據(jù)場景值的差異化加載不同的文件,并且以默認(rèn)文件作為降級兜底。不同場景對應(yīng)不同的入口文件,利用這種形式可以有效控制包體積。
4.3.3 一站式多環(huán)境融合
一個正常的研發(fā)流程是從 test 環(huán)境,到 uat 環(huán)境,再到 live環(huán)境。Code Push Platform 對接了 App 的 test/uat/live環(huán)境,所以 RN 開發(fā)者只需要在該系統(tǒng)就可以進(jìn)行“一站式”的操作,方可滿足一個需求的整個研發(fā)周期。
不同環(huán)境的包資源流轉(zhuǎn),是多環(huán)境融合的一大亮點。如果某 RN bundle 在 uat 環(huán)境構(gòu)建,它也不需要重新構(gòu)建,將 bundle 無縫轉(zhuǎn)換到 線上 環(huán)境進(jìn)行發(fā)布。它帶來的優(yōu)勢在于“零構(gòu)建時長”以及資源包的穩(wěn)定性,因為 bundle 沒有重新進(jìn)行構(gòu)建,所以它的內(nèi)容已經(jīng)在 uat 得到了充分的驗證,發(fā)布風(fēng)險更小。
5. 舊業(yè)務(wù)的遷移方案
如何遷移現(xiàn)有業(yè)務(wù)的 App 是一個非常嚴(yán)肅的問題,特別是歷史背景較重的業(yè)務(wù),它們可能存在“邏輯耦合”或者“組件耦合”的場景。與此同時,很多相關(guān)業(yè)務(wù)都在需求迭代當(dāng)中,系統(tǒng)的遷移是不能阻礙需求迭代,所以舊業(yè)務(wù)“漸進(jìn)式遷移”方案是非常必要的。
5.1 邏輯耦合
如果兩個以上 plugin 存在邏輯依賴關(guān)系,用戶必須同時加載到最新的 plugin??紤]到熱更新失敗的可能性,邏輯耦合就是多個 plugin 隱藏著一種約束關(guān)系。例如,訂單業(yè)務(wù)和購買業(yè)務(wù)存在一定的邏輯耦合關(guān)系,發(fā)布負(fù)責(zé)人針對流量極大的超級 App,不可能逐個發(fā)布 plugin。在極端的狀態(tài)下,用戶可能會先加載到 plugin A,新版本的 plugin A 和舊版本的 plugin B 是不兼容的,這樣會帶來嚴(yán)重后果。遇到這種情況,有兩種解決方案:
方案一:plugin 間邏輯解耦,保證每個 plugin 的獨立性。
方案二:系統(tǒng)提供了聯(lián)合發(fā)布,在 Native 側(cè)保證多個 plugin 能夠同時加載到最新。
方案一是最理想化的狀態(tài),但是在業(yè)務(wù)場景細(xì)分的情況下,項目結(jié)構(gòu)很難做到絕對獨立。
針對老業(yè)務(wù)可以考慮方案二,系統(tǒng)提供了 module 的概念,一個 module 對應(yīng)著兩個以上的 plugin。它們存在著一個綁定的關(guān)系。在同一個下載任務(wù)里面,客戶端 SDK 以“事務(wù)”形式,保證多個 plugin 能夠同時下載完成并投入使用。聯(lián)合發(fā)布這個能力在系統(tǒng)層面,有效規(guī)避這種錯誤的可能性。
5.2 組件耦合
如果說聯(lián)合發(fā)布是針對在 plugin 維度的“邏輯耦合”兼容方案,“組件耦合”則是更細(xì)粒度的組件級別的耦合關(guān)系。也就是說,一個頁面中存在多個組件來自不同的團(tuán)隊,例如商品詳情頁等頁面有評價功能組件。這種“一個頁面存在著 JSContext 相互嵌套”的情景存在于電商業(yè)務(wù)當(dāng)中。
針對這種“組件耦合”情況,有兩種解決方案:
方案一:嵌套組件抽離成為一個獨立倉庫,供第三方 plugin 使用。
方案二:使用“同屏渲染”的能力實現(xiàn)“多 Context 嵌套”。
方案一是最理想的解決方案。但是考慮到遷移成本,我們也提供了方案二(一種“同屏渲染”嵌套組件)來支持這種場景,它類似一種 Native 組件。在多個 JSContext 的情況下,通過 plugin 名和頁面名將所需要的內(nèi)容嵌套到另一個頁面當(dāng)中。
如下圖所示,plugin A 會嵌套 plugin B 的內(nèi)容,A 和 B 也可以實現(xiàn)在同一個屏幕進(jìn)行渲染。從 Web 的方向理解,這種情況有點像 “iframe” 的場景,支持多個頁面的嵌套。它非常易于 RN 開發(fā)者的理解,客戶端 SDK 能夠動態(tài)加載目標(biāo) bundle 并將它渲染在合適的位置。
5.3 漸進(jìn)式遷移
對于現(xiàn)有的 App,因為業(yè)務(wù)沒法暫停迭代,我們難以一次性完成整體遷移。因此,我們提供了“漸進(jìn)式遷移”方案。考慮到歷史背景,該方案不會一次性把所有 plugin 都遷移,而是逐步拆分,再遷移接入到新發(fā)布系統(tǒng)。
遷移的步驟如下圖所示:
優(yōu)先將獨立的業(yè)務(wù)遷移到 Code Push Platform,它們享用一個獨立的 JSContext;所有“待拆分代碼”共用一個獨立的JSContext;將“待拆分代碼”繼續(xù)拆分成幾個獨立 plugin,獨立使用 JSContext,其他內(nèi)容則保持步驟二的狀態(tài)。
隨著版本迭代,重復(fù)第二和第三步驟,直至歷史業(yè)務(wù)全部拆分完畢。這樣我們可以達(dá)到一個最優(yōu)的目標(biāo),即是真正意義的“獨立構(gòu)建”和“獨立發(fā)布”。
6. 總結(jié)
該系統(tǒng)的目標(biāo)在于滿足所有 App 的多團(tuán)隊研發(fā)協(xié)作效率問題,去中心化 RN 發(fā)布模型考慮到“獨立運行時”、“獨立開發(fā)”、“獨立構(gòu)建”、“獨立發(fā)布”四大方面,保障了每個 plugin 運行的獨立性。最終目標(biāo)在于支撐 Shopee 的多個 RN 團(tuán)隊在不同 App 平臺根據(jù)自己節(jié)奏自由發(fā)布且高效運作。
系統(tǒng)設(shè)計涉及到“多團(tuán)隊權(quán)限管控”、“客戶端版本控制”、“灰度和回滾”、“增量差分”、“多入口包體積優(yōu)化”、 “一站式多環(huán)境融合”,加速了整個研發(fā)流程,真正做到了“靈活性”和“穩(wěn)定性”的兼得。
到此這篇關(guān)于Shopee在React Native 架構(gòu)方面的探索的文章就介紹到這了,更多相關(guān)React Native 架構(gòu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳細(xì)談?wù)凴eact中setState是一個宏任務(wù)還是微任務(wù)
學(xué)過react的人都知道,setState在react里是一個很重要的方法,使用它可以更新我們數(shù)據(jù)的狀態(tài),下面這篇文章主要給大家介紹了關(guān)于React中setState是一個宏任務(wù)還是微任務(wù)的相關(guān)資料,需要的朋友可以參考下2021-09-09Can't?perform?a?React?state?update?on?an?unmoun
這篇文章主要為大家介紹了Can't?perform?a?React?state?update?on?an?unmounted?component報錯解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12React如何使用sortablejs實現(xiàn)拖拽排序
這篇文章主要介紹了React如何使用sortablejs實現(xiàn)拖拽排序問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01Redux DevTools不能顯示數(shù)據(jù)問題
這篇文章主要介紹了Redux DevTools不能顯示數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01JavaScript的React框架中的JSX語法學(xué)習(xí)入門教程
這篇文章主要介紹了JavaScript的React框架中的JSX語法學(xué)習(xí)入門教程,React是由Facebook開發(fā)并開源的高人氣js框架,需要的朋友可以參考下2016-03-03基于React.js實現(xiàn)原生js拖拽效果引發(fā)的思考
這篇文章主要為大家詳細(xì)介紹了基于React.js實現(xiàn)原生js拖拽效果,繼而引發(fā)的一系列思考,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-03-03