淘寶Native研發模式的演進與思考
DX的起源與發展
DX是從首頁為解決業務兩端一致性和動態性的問題孵化而來,后經過若干次升級和迭代,逐步完成了從首頁動態化方案——基礎鏈路動態化方案——集團共建動態化方案——集團移動小組標準Native研發模式的多級跳躍。隨著接入的業務和開發者越來越多、使用場景越來越復雜和多樣,技術范疇也從端側SDK延展到動態化技術體系,再到Native研發模式。
淘寶的技術選型
這可能是當前這個星球上最復雜App的淘寶技術選型標準,我們把淘寶所有的業務分為三個大類:核心域、導購域和開放域。不同場景對于性能體驗、穩定性、交付效率都有不同的訴求,這里沒有“技術銀彈”,只有適合目標場景的最佳方案。
過去兩年我們面臨的挑戰是什么
作為核心域標準技術選型的DX,過去幾年的挑戰是什么呢?
首先來看一下大的業務背景:
- 業務競對加劇,集團從航母戰略升級為App矩陣戰略,對交付量和效都有了更高的要求;
- 隨著直播/短視頻/AR/3D技術的發展與普及,移動端邁入沉浸式/富交互體驗,Native技術棧的優勢更加明顯;
- 淘寶的購物效率越來越高,用戶訪問時長/頻率降低,大部分購買決策發生在淘外,淘寶逐漸淪為下單/比價工具,而當內容場掌握了大量入口流量和用戶時長后,開始下場自己閉環做電商成交,曾經的引流渠道變成直接競爭對手,淘寶也將面臨“無源之水、無本之木”的困境,所以淘系近年來一直在打造自己的內容/社區,讓更多的消費購前決策發生在淘系。
這三大業務背景深刻地影響著接入DX的每個業務以及潛在的新業務,所以過去兩年,我們的工作主要圍繞研發模式升級和體驗升級來進行。
首先來說研發模式升級,原先主要有兩個問題:
1、DX自身技術體系不完整,在19年之前,DX只能說是一個客戶端動態化方案,包含一個端側SDK和一個組件平臺,但隨著接入的新業務越來越多,DX自身技術體系不完整的瓶頸愈發明顯,大量新業務需要重復建設端側容器、搭建平臺等基礎設施,導致接入成本高、收益低,而各業務自建的容器和平臺,又難以沉淀和復用,進一步加劇了后續技術拉通的難度。
2、研發模式過于單一,隨著沉浸式體驗和富交互體驗的鋪開,很多業務告別了過去重展示輕交互的模式,往中度交互開始進行體驗升級,尤其以微淘拆分出的逛逛和訂閱為代表的類內容場,需要以更沉浸式的瀏覽/互動體驗來吸引用戶更多的停留和內容消費,而我們過去基于搭建的方式并不能很好地對此類場景進行研發支撐。
再來是體驗升級,我們雖然并不能決定具體業務場景的視覺交互和體驗設計,但面向優秀的體驗,我們可以做幾個事情:
- 提供開箱即用的體驗優秀的標準化組件,如日歷/富文本/復雜嵌套容器/Page Indicator等組件,極大降低體驗精細化帶來的適配成本,如Dark Mode/銀發版字體自動放大適配等;
- 在富媒體/端智能的快速發展,以及下沉市場中低端設備的增加的背景下,持續優化DX渲染管線的性能水位,提升廣大業務場景的基礎瀏覽和交互體驗;
- 提供更完善的性能工具,幫助開發者在面對相對“黑盒”的模版性能問題時,可以做到早發現、可排查、可優化。
關于ProCode和LowCode的思考
在我們承接新訂閱和逛逛的新場景時發現,在這種內容型交互類場景,有著豐富的交互、聯動與轉場,DX過去動態邏輯表達力不足和基于模塊搭建的LowCode研發模式無法支撐其進行高效研發,核心矛盾是動態邏輯、復雜容器及內部聯動、多人并行開發與模塊引用,于是經過一系列的討論和思考后,有了下面這張圖:
基于在新場景下面臨的問題,我們決定把研發模式進行分層,定制性強/動態邏輯要求高的業務場景,用ProCode模式來開發,通過強大專業的DX IDE+Git來進行工程化多人協作開發,可以進行UI和動態邏輯的調試(事件鏈),而定制性弱/復用性高的業務場景,用LowCode模式來承接,通過可視化搭建平臺來拖拽生成視圖和布局,配置相應數據源和規則。同時,不斷抽象沉淀組件庫/能力庫,使越來越多的交互/功能/布局可以通過可視化方式來拖拽生成。
我們暢想的未來是,當業務同學有一個新的業務想法時,可以通過LowCode模式自行搭建與配置,在線上圈選小部分用戶進行灰度驗證,在驗證產品方向正確后,再投入更多的專業技術人員來做進一步的產品定制和升級,來產生更大的業務效果。
ProCode “三駕馬車”
要讓 ProCode 模式成型,必須要解決的是動態邏輯、復雜容器和相應的研發支撐問題,所以就有了 ProCode的“三駕馬車”:IDE、列表容器、事件鏈。
IDE
第一是IDE,這里補充些背景信息,在做IDE之前,DX有個模版平臺,主要來承載模版的開發/調試/發布工作,那我們為什么要再做一個IDE呢?
首先,嚴格來說,原來DX平臺上的不叫IDE,只能說是Editor,從關系上來看,IDE包含Editor,借用一張VSCode的圖來說明兩者的差異:
其次,原來DX平臺上的Web Editor(Monaco)雖然也支持大部分LSP API,但沒有工程/項目體系、文件系統、代碼上下文理解、調試等能力,且受限于瀏覽器環境,而IDE運行于本地環境,有豐富的擴展能力和插件,可以提供完整的研發生命周期支持。
所以,在ProCode模式下,我們的技術選型基于Katian IDE(兼容VSCode API),來得到更多的視圖擴展能力,未來還能通過靈活的部署方式(遠程開發、云端部署),實現更高效/快速的研發與協作。
目前DX IDE已經接入了包括淘寶在內的集團多個重要業務,這些開發者使用IDE后研發效率有了大幅的提升,尤其是多人協作與調試效率有了量級的提升。
列表容器
第二是列表容器,過去基于搭建的容器有個特征,容器/頁面布局由數據協議來描述,即數據布局一體,這在簡單頁面結構(如單列表頁面)場景下運轉良好,配合搭建能較快地配置出樓層,通過協議來驅動端側渲染,服務端也不需要過多感知業務結構(默認就是單列表布局)。但在復雜頁面結構(如多層多Tab嵌套容器),尤其還有復雜交互的情況下,就會讓整個數據結構變得過于復雜,同時也增加數據協議的paylaod,而頁面布局的變化頻率遠小于業務數據。
在這種場景下,把兩者耦合在一起,明顯是不合適的,于是,出于職責單一和“動靜分離”的原則,我們進行了重新的思考和設計,在模版XML中統一描述布局(包括容器/頁面布局),讓數據回歸純粹的數據結構,與此同時,我們也在DX中內置了新的功能強大的列表容器,寫起來就像下面這樣:
<RecyclerLayout
userId="recycler"
width="match_parent"
height="match_parent"
dataSource="@data{itemList}"
backgroundColor="@triple{@getEngineStorage{backgroundColor}, @getEngineStorage{backgroundColor}, '#ffffff'}"
columnCount="2"
columnGap="4"
isOpenPullToRefresh="True"
refreshPullText="下拉即可刷新..."
refreshReleaseText="釋放即可刷新..."
refreshLoadingText="加載中..."
loadMoreFailText="加載失敗"
loadMoreLoadingText="加載中..."
loadMoreNoMoreDataText="到底了"
onPullToRefresh="@dxEventHandler{'refreshTest'}"
onEndReached="@dxEventHandler{'loadMoreTest'}"
>
<Template
if="@subdata{span}"
sticky="True"
colspan="2"
name="aa"
>
<TextView
width="match_parent"
height="100"
text="@data{text}"
textSize="15"
backgroundColor="#FFFF55"
borderColor="#FF9911"
borderWidth="2"
/>
</Template>
<Template
if="@subdata{recyclerInsertItems}"
name="recycler_sub_template1"
version="2"
/>
</RecyclerLayout>
目前列表容器已覆蓋了多個淘寶和集團內重要業務場景,支持了嵌套容器/聯動框架/動態Template/Slot等能力,下一年我們也會推廣到更多的業務。
事件鏈
第三是事件鏈,過去DX只支持UI的動態化,有限的邏輯控制只能通過表達式來實現,其他的邏輯變更都依賴發版,而隨著業務對發布上線的時效要求越來越高,邏輯動態化的訴求也愈發強烈。于是,面對這個訴求,我們經過反復的討論和思考,綜合考慮了蘋果合規、性能、穩定性、開發效率,提供了事件鏈的方案。
在事件鏈方案中,開發者在XML對應的eventchain.json中使用原子能力和表達式進行邏輯組裝,在研發期IDE提供了事件鏈JSON的智能感知能力(包括補全/提示/跳轉/格式化/折疊/高亮等),可以通過事件鏈回放功能,來圖形化顯示事件鏈執行的流程以及每個原子能力的出入參和上下文,來便捷問題的排查。開發完成后,在編譯階段,DX編譯服務會把該JSON編譯成二進制,與模版二進制一起下發,確保性能和一致性,在端側有事件鏈引擎來負責二進制的解析,以及原子能力的調度。
關于性能優化的思考和實踐
過去兩年,DX的能力越來越強大,功能越來越豐富(動畫動態化、事件鏈、復雜嵌套容器、直播/視頻組件、3D組件等),那DX一直以來的性能優勢,會不會有所影響或下降?我們又是如何在不斷加入新功能的同時,做到性能不退反升呢?面對越來越復雜的模版,如何幫助開發者在面臨性能問題時,有排查和優化的抓手,而不是一無所措呢?
這就要說下DX的渲染管線,如果說Flutter是接管整個系統渲染的革命派,那DX就是基于原生系統渲染的改良派。DX的主要設計思路是基于Platform First來改良系統原生渲染管線/機制中的性能瓶頸和問題,并做到兩端統一。
從DX管線各階段的耗時分布來看,主要大頭在load和render,所以我們主要通過異步化管線來解決load的耗時問題,通過異步繪制和高性能組件來解決render的耗時問題,通過計算資源管控來盡量保障資源的合理分配。
異步化管線和異步繪制,主要是讓更多工作能夠在子線程執行,或者并行調度,降低主線程的負擔,合理利用多核計算資源,在DX列表容器中,我們內置了該能力,可以做到開箱即用,而在業務容器中,需要業務方按照我們建議的時機/業務認為合適的時機來調用相應API,來獲得異步化的調度。
高性能組件方面,我們主要優化了圖片組件和自研了富文本組件,尤其是富文本組件,我們基于系統TextKit/TextLayout實現了自測自繪,提供了TextSpan/ImageSpan/展開收起等能力,相比原生組件,內存降低(iOS 70% Android 8%),性能提升(iOS 20% Android 10%)。
計算資源管控方面,我們主要設計和實現了離屏計算資源管控框架,用來實現視頻/直播/動圖的播控問題,且提供了規則配置能力,在多視頻情況下,iOS CPU占用率降低66%/Android降低25%,目前在淘寶的一些重要信息流場景已上線。
性能工具方面,過去我們主要是在運行時進行各種底層優化和編譯期的性能預警提示,在遇到模版性能問題時,通常需要業務方和DX同學分別打Log來看時間的消耗和定位問題的所在,雙方都耗時耗力。會出現這種情況,一部分原因是DX與Native的結合度較高,一次渲染過程中有大量的DX內部代碼和業務方自定義代碼交織在一起,導致無法快速精準定位問題所在;另一部分原因是DX的渲染過程對業務方來說相對黑盒,業務方也沒有進行排查和優化的有效抓手。
為了擺脫這種場景,我們要讓整個模版渲染過程在開發階段進行一定的白盒化,以可視化圖形方式展現模版各階段的渲染耗時及其歸屬(DX內部還是業務方自定義部分,具體到是哪個Widget的什么階段/DataParser/EventHandler),同時通過不斷累積的常見性能問題,進行模版自動分析并提供一定的優化建議。
技術大
總結一下,這是目前DX技術體系的大圖,研發支撐層,我們通過IDE和平臺來滿足ProCode和LowCode兩種生產模式的訴求,核心運行時層,我們不斷增強容器/組件/布局等視圖能力的同時,引入一定的動態邏輯能力,通過共建不斷沉淀原子能力庫,業務框架層,我們也與淘寶內團隊合作,把復雜狀態管理、流程編排等能力引入DX技術生態中,業務可按需接入。
未來展望
從技術演進上,我們認為DX的下一階段,要實現三化:
- 標準化,目前DX的DSL偏向私有標準,開發門檻較高,不利于擴大開發者群體,接下來我們要向客戶端行業標準靠齊,吸引更多的開發者;
- 現代化,目前DX通過命令式來描述UI,而行業已經向聲明式邁進,從技術演進路線上,我們也要擁抱聲明式,進一步提升開發體驗和效率,此外,研發支撐方面,我們這個財年從0到1打造了DX IDE,在本地進行安裝和運行,已經能較大程度提升復雜頁面/模版的研發效率,但我們不會停下研發提效的腳步,接下來還要嘗試通過云端部署的方式,達到免安裝/開箱即用/工作空間共享等,進一步提升研發和協作效率;
- 一體化,目前我們已經通過DX IDE實現了部分的工程一體化,未來為了進一步提升前后端協作效率,也為了把Serverless打造成客戶端的基礎能力之一,我們要實現端云一體,在一個集成研發環境中,以同一個技術棧,同時開發端側和云側代碼,簡化端云遠程調度開發成本,發布期通過編譯技術部署到不同的目標,提升前后端協作效率和端側技術方案自主性。