提升 Hybrid 體驗:餓了麼雙十一 PHA 框架技術實踐
作者:逍菲、崖松、子倫
餓了麼端 618、國慶、雙11、雙12等大促會場基本上會標配底部導航,在之前普通H5容器中底部導航是前端實現,每次點選會場底部導航的tab,都會重新啟動一個活動頁面覆蓋在上面,即使之前開啟過的tab也都要重新建立和載入,體驗不佳,且H5也不能很好的結合Native能力做進一步的體驗和效能優化。
經過調研發現手淘PHA框架可解決上述痛點問題,PHA容器底部TabBar為Native渲染,tab點選時底部bar不會重建,tab對應的webview在整個PHA容器中也可以平滑過渡、無縫切換,無需另起容器。且載入過的tab活動頁面Webview會常駐記憶體,當再次訪問時會直接切換至前臺,更接近native體驗。
在去年 618、國慶、雙11和雙12大促中,結合餓了麼業務特性又陸續落地了一些特色優化手段,帶來了更好的效能體驗和業務成果。
雙十一上線效果
效果影片檢視請點選:餓了麼雙十一 PHA 會場實踐
老容器和pha容器對比。其中左側為老容器會場,右側為pha容器會場。
PHA 簡介
什麼是 PHA,PHA 全稱 Progressive Hybrid App,是提升 Hybrid 體驗的一種新框架,提供了一些 Native 同層元件以及漸進式增強策略來建立 Hybrid APP 應用,讓這些應用具有與 Native 相同的使用者體驗。
- PHA 使用了 Web Application Manifest的配置並且對配置進行了功能擴充套件
- 每個 PHA 應用都會啟動一個 App Worker,Worker 是獨立於當前頁面執行在客戶端裡的一段 JS 指令碼。可在 Worker 中定製業務邏輯,如基於 LBS 請求底部Tab展示的資料列表
- 應用下可以有多個頁面,每個頁面的預設渲染引擎是 WebView
- 每個頁面中 PHA 提供了像下拉重新整理、頁頭等 UI 能力,都可以通過在 Manifest 中定製
- 針對應用 PHA 還提供了 Tab 容器、Swiper容器、啟動屏等 UI 能力和預請求、離線快取等效能優化能力,可通過在 Manifest 中配置實現
pha架構圖
餓了麼接入方案
本地生活跟淘寶等業務主要的區別為前者強依賴LBS屬性,包括底部 Tab、商品、品牌等資料的召回。因此需要在使用者開啟PHA框架時,執行定位並載入對應的底部 Tab、頂部橫滑資料後,動態組裝出對應的manifest.json 資料來渲染PHA,整體架構圖如下:
架構圖
B端鏈路
墨斗平臺依賴天馬原始碼頁面服務來建立會場框架,沿用墨斗資料搭建來配置底部 Tab 和 頂部Swiper 的資料,實現定投,主要流程如下:
注:“墨斗”是一個模組搭建平臺,“天馬”可以提供基礎搭建服務。
資料搭建
資料搭建是墨斗中提供的資料多排期定投能力,配置定投併發布後可得到當前配置的唯一資源位ID, 業務可通過非同步介面獲得配置資料。
會場框架工具
新增會場框架工具,工具提供了會場框架建立、釋出及編輯能力,建立併發布框架後即可得到框架投放連結。
C端鏈路
C端鏈路由客戶端結合前端實現,主要包含以下3部分:
1、客戶端PHA框架:集成了 Tabs 容器、Swiper容器、啟動屏、Appworker、預載入等框架核心能力
2、原始碼模版頁:作為PHA框架的入口,同時提供 PHA 正常載入入口 (native.xtpl) 和降級入口 (web.xtpl)
3、AppWorker 服務:能在框架渲染前有機會根據業務訴求定製 manifest,包括頭部橫滑容器的 UI 定製,事件處理、埋點等
整體載入鏈路圖
客戶端接入改造
路由層
改造餓了麼路由層,增加pha入口,並在入口處增加native的降級策略、灰度控制等邏輯。
介面卡:native導航頭適配
由於剝離了手淘的ui庫,餓了麼需要自行實現導航欄的: 顯示、隱藏、各類主題、滾動變色、title設定、logo設定、事件回撥、降級跳轉等,此外雙端也提供了一些導航頭、狀態列等私有jsapi供前端進行呼叫。
iOS端通過PHANavigationBarProtocol實現對應方法,Android端以系統Toolbar為基礎,定製餓了麼的TranslucentToolbar,以Fragment的形式嵌入到PHA容器Activity中。
下圖是native端導航頭,title支援設定文字、載入圖片,導航欄主題支援透明、白色:
文字&圖片導航欄
下圖是light和dark風格的狀態列:
light&dark狀態列
介面卡:圖片載入適配
餓了麼iOS端由於自己維護了圖片庫,需要對圖片載入進行適配,實現PHAImageLoaderProtocol及其圖片載入相關方法。
淘系依賴剝離
餓了麼對於包大小有嚴格的控制,對於新引入的二三方庫稽核嚴格。餓了麼端接入pha較早,早期版本中存在額外的手淘依賴,且有些功能餓了麼暫時未用到,或已有類似的實現方案,因此需要對部分手淘依賴庫進行剝離,對不需要的功能進行剪裁。
安卓剝離的直接依賴庫約20個,如:直播庫、公共資源庫、ui庫、compat庫、啟動框架庫、atlas等,總大小約:7.2M+,涉及檔案改動個數:100+
注:目前安卓最新的官方2.0版本已剝離了這些依賴,大大的贊!iOS端在一接入pha時依賴已基本剝離,無需大的改造。
前端接入改造
框架服務
1、原始碼模版:作為PHA框架的入口,同時提供 PHA 正常載入入口 (native.xtpl) 和降級入口 (web.xtpl)
- 正常入口:基於平臺建立會場框架時錄入的資料生成manifest 半成品,結果中包含 AppWorker 地址,建立框架時的配置資訊等,客戶端執行 AppWorker 檔案後,啟動PHA框架渲染
- 降級入口:為不支援PHA的低版本或PHA建立失敗時提供統一的降級處理,用普通H5容器開啟投放連結中的 downgradeUrl 地址實現降級,保證業務可用
2、AppWorker 服務:能在框架渲染前有機會根據業務訴求定製 manifest,主要提供如下能力
- 定位、請求底部bar、頂部swiper資料
- 頭部橫滑容器的 UI 定製、事件處理
- 和PHA的事件、訊息處理
- 構建最終的 manifest 檔案
- 啟動框架渲染
- 埋點等
會場鏈路
1、頁面solution、基礎util、componet 改造
2、多個模組改造
- 透明頭模組:之前透明頭是H5結合Native來實現的,業務定製能力相對較弱,為了解決這個題,針對PHA容器實現了純H5版的透明頭模組
- 底部導航模組、搜尋模組、分享模組:相容PHA 和非PHA容器
埋點改造
PHA框架提供一套埋點API,客戶端和前端需根據自己的埋點方案進行適配改造,統計和上報口徑跟非PHA下會場埋點保持一致,主要有以下兩點:
- 需在每次切換頁面時上報PV,包括已經瀏覽過的頁面,如Tab A 切到Tab B 再切到Tab A 時,在最後的Tab A 頁面需要觸發PV上報
- 底部 Tab 或頂部 Swiper 容器切換不同 Item 時上報點選埋點
效能優化
餓了麼端pha容器載入流程主要為下圖四個階段,針對不同階段可進行對應的效能優化手段。
- pha容器階段:半成品manifest和worker可進行預取或快取優化;完整manifest資料請求組裝階段可進行經緯度的預取和介面的預請求;
- webview階段:webview可進行預初始化,主文件載入可以用模版化方式進行優化;
- 業務階段:針對業務js、css資源可進行資源離線和快取,對常用js資源可進行內建;對業務介面資料可進行預請求;預熱和預渲染。
官方PHA框架優化手段
pha框架本身提供了manifest預請求和快取、html模板化、離線資源存取、內建Js預渲染等優化手段。
以iOS端雙十一會場為例,使用manifest和worker預取,優化容器建立時間200ms以上。
餓了麼端特色效能優化
PHA webview預熱
開啟pha首屏頁面和切換tab時需要執行容器建立和主文件載入,有較長時間的白屏,體驗不佳。預渲染頁面無疑是效能效果最佳手段,但一方面會造成資源浪費,另一方面也會帶來很大的記憶體壓力,不能隨意濫用,後續若能和端智慧更好結合或許才能更好的發揮它的價值。
餓了麼端對首屏外的tab採用了預熱方案(預熱頁面最大個數可配置),來消除tab切換時的白屏時間。預熱和預渲染的不同點為:預熱在離屏階段拿到主文件後不發起首屏介面請求。在首屏頁面渲染完成後,將剩下的底部和頂部item對應的webview都預熱並快取好,當用戶點選對應tab時再消費webview進行上屏操作,具體實現複用pha/preload子模組鏈路並進行了一些改造,參考了TSchedule的預渲染邏輯。主要流程如下圖所示:
優化前後鏈路對比:
下面具體展示預熱效果(左側為未開啟預熱,右側為開啟預熱狀態):
效果影片請點選檢視:餓了麼雙十一 PHA 會場實踐
業務介面預載入
會場頁面的首屏介面耗時較長,部分甚至達2s以上,為了縮短頁面載入耗時,對首屏介面進行預請求。考慮到餓了麼端之前已通過TSchedule支援了介面預請求的能力和包大小問題,因此決定不引入pha的prefetch模組,而是藉助餓了麼現有能力對pha進行data prefetch的適配化改造。
由客戶端同學提前釋出orange(開關配置釋出平臺)預請求配置,pha會場在首屏路由、底部tab切換、頂部pageheader切換時提前觸發介面預請求,也提供jsapi供前端自行呼叫預請求,優化效果明顯,目前已經是餓了麼端會場業務必備優化項。
(1)、首屏webview載入前網路資料非同步預請求
(2)、啟動前路由階段網路資料非同步預請求
(3)、tab切換webview載入前網路資料非同步預請求
(4)、通過jsbridge在webview載入前網路資料非同步預請求
完整manifest資料請求優化
本地生活業務多對定位有強依賴,對於會場業務目前只能先返回半成品manifest檔案,在worker中呼叫端上jsapi拿到經緯度後發起介面請求,獲取完整manifest相關資料進行組裝;對於非會場的單頁面業務進行介面請求時也大都需要經緯度資訊。
經緯度預取
方式一: 將經緯度資訊注入半成品manifest(針對需要pha worker對半成品manifest檔案進行處理的業務場景)
在回傳給前端的manifest半成品json中,追加上首頁快取的經緯度資料,省去一次jsbridge呼叫耗時。優化前後流程對比如下圖所示:
追加的經緯度資訊模板:
方式二: 在入口連結處傳入經緯度(針對無需pha worker的場景)
大多單頁面業務在一開始就能獲取到完整的manifest資料,不需要worker進行額外處理,也就無法直接通過方式一獲得注入的經緯度資訊,因此我們採用了另一個方案,給pha入口連結(manifest url)拼接經緯度引數,並通過pha引數透傳的能力將其拼接到H5內頁的連結上。具體流程如下:
完整manifest資料介面預請求
在worker獲取經緯度後,會使用頁面id、資源id和經緯度發起一個介面請求,獲取完整manifest所需資料(pages、tab等),資料返回後拼接成完整manifest資料,然後呼叫jsapi通知端上更新manifest,進行tab和頁面的載入,此介面耗時大概200ms左右。
通過在路由階段預請求此介面來進行優化,由於介面所需的頁面id和資源id針對同一大促會場投放期間基本不會改變,因此提前釋出的預載入配置將這兩個id直接設為固定值,而經緯度在預請求發起時動態獲取。
此功能上線之後經過兩個版本的對比,首屏耗時整體減少約150ms。
穩定性
降級
PHA框架渲染前的任何一步出錯可能導致框架初始化失敗,為實現同一個連結投放到餓了麼新老版本,並保證業務在各種異常場景下可用,需做到無縫降級到H5容器,流程如下:
預案
在整個雙十一大促開始前,針對pha會場可能出現的各種異常情況進行了預案的錄入和演練,例如:會場降級,預熱關閉、介面預載入關閉、manifest預取關閉等,在安全團隊和測試團隊同學的共同支援保障下,pha容器會場在大促期間執行穩定,未出現任何重大故障。
監控和報表
在dp2監控平臺構建了manifest命中率監控、離線資源命中率監控、降級監控、效能監控、預熱命中率監控、首屏耗時監控、容器內webview載入耗時監控、白屏監控等相關報表。
此外手淘目前在建設容器大盤,ios端已經初步進行了接入,期待大盤後續例如告警,二方業務資料展示等建設。
總結和展望
通過pha框架的接入及一系列的優化手段,雙端優化首屏耗時減少650ms左右,消除了切換tab時的白屏時間,提升了餓了麼端大促會場的使用者體驗,同時也促進了業務資料的提升。
據運營側資料統計,去年雙十一主會場底部導航Tab的曝光點選率,相比國慶大促正式期提升超過50%。自618大促正式期試點PHA以來,新底部導航點選率持續上升。
後續前端需解決拼連結的問題作為常態化的產品能力支援各業務場景使用,端側也將進一步探索其他的優化方案,比如: Tabbar直出渲染、pha容器Fragment化、quickjs引入等。此外除會場業務,餓了麼端超會部分業務也進行了pha投放,取得了一些效能上的收益。
期望未來餓了麼所做的優化點能給大家一些啟發與思考,服務更多合適業務,進一步提升使用者體驗。
關注【阿里巴巴移動技術】微信公眾號,每週 3 篇移動技術實踐&乾貨給你思考!
- Android Target 31 升級全攻略 —— 記阿里首個超級 App 的坎坷升級之路
- 系統困境與軟體複雜度,為什麼我們的系統會如此複雜
- 系統困境與軟體複雜度,為什麼我們的系統會如此複雜
- Cube 技術解讀 | Cube 渲染設計的前世今生
- 淘寶Native研發模式的演進與思考 | DX研發模式
- 大量模組殼工程本地如何快速編譯?優酷 iOS 工程外掛化實踐
- 大量模組殼工程本地如何快速編譯?優酷 iOS 工程外掛化實踐
- 從0到1,IDE如何提升端側研發效率?| DX研發模式
- 優酷移動端彈幕穿人架構設計與工程實戰總結
- 2022 支付寶五福 |“聯機版”打年獸背後的網路技術 RTMS
- Flutter 圖片庫重磅開源!
- 如何持續突破效能表現?DX 效能優化策略詳解
- 淘寶Native研發模式的演進與思考 | DX研發模式
- 前車之鑑:聊聊釘釘 Flutter 落地桌面端踩過的“坑” | Dutter
- 釘釘 Flutter 跨四端方案設計與技術實踐 | Dutter
- 前車之鑑:聊聊釘釘 Flutter 落地桌面端踩過的“坑” | Dutter
- Dutter | 前車之鑑:聊聊釘釘 Flutter 落地桌面端踩過的“坑”
- Dutter | 釘釘 Flutter 跨四端方案設計與技術實踐
- Swift 首次除錯斷點慢的問題解法 | 優酷 Swift 實踐
- Swift 首次除錯斷點慢的問題解法 | 優酷 Swift 實踐