天天看點

為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望

作者:閑魚技術-頌晨、玉缜

前言

出于多端投放和開放生态的考慮,閑魚開始接入整個阿裡小程式體系。閑魚在9月份迅速上線了第一個小程式魚塘小程式,由于剛接觸不熟悉小程式體系,整體性能上有比較大的優化空間,主要表現在以下問題:

  • 小程式加載慢,低端 Android 機(Android vivo Y67)上首屏時間接近6s
  • 滾動卡頓,在 iPhone 7P 上滾動幀率平均在 40fps 左右
  • 滾動多屏資料之後 Tab 點選切換慢,在 iPhone 7P 上切換 Tab 等待時間 3-5 秒,瞬時幀率低于 30fps

    小程式由于其邏輯和渲染分離的架構特點,除傳統 H5 優化手段之外還有其他不同點。本篇文章主要分析小程式構架對渲染的影響,以及魚塘小程式下具體優化手段。

小程式架構

在分析具體優化 Case 之前我們先看下小程式架構,先要了解架構才能清楚如何去優化具體的業務代碼。阿裡小程式采用支付寶小程式的架構,這裡引用一張支付寶小程式頁面生命周期圖。

為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望

目前市場上所有小程式都采用邏輯(worker)和渲染(webview)分離的方式。這樣帶來的好處是:

  1. 能夠滿足對于外部應用管控的訴求,由于業務邏輯沒有運作在 webview 上,是以無法通過浏覽器的API直接操作渲染動作,意味着不能通過腳本做一些動态化操作。所有渲染相關操作都是通過 axml 來定義,外部應用進行更改都需要通過平台稽核。
  2. 多個頁面可以共享同一個 JS 運作環境,整個小程式生命周期可以共享全局應用上下文,接近 App 的開發體驗。
  3. 頁面渲染與業務邏輯分開執行,不會出現 JS 邏輯執行導緻渲染卡住的情況,有利于提升整體渲染性能。

但是這樣也會造成一個顯而易見的問題,頁面性能強依賴 webview 和 worker 的通信效率:

  1. webview 和 worker 之間的通信是異步的。這意味着當我們調用 setData 時,資料變更不會立即展現到頁面渲染上,而是需要從 worker 異步傳輸到 webview 。
  2. 資料傳輸時需要序列化為字元串,然後通過 evaluateJavascript 方式傳輸,資料大小會影響傳輸性能。

小程式邏輯和渲染分離的架構造成它與傳統 H5 性能優化方式上的一些差異。小程式性能優化可以參考看下官方推薦的一些

性能優化建議

,簡單來講需要特别注意 setData 操作的頻次和傳輸資料,接下來我們結合魚塘小程式具體案例一塊探讨下。

業務層優化

魚塘小程式是一個類似興趣圈子下的内容聚合場景,使用者在這裡可以無限加載浏覽内容,還會點選 Tab 切換浏覽不同次元的内容。我們需要重點考慮小程式加載流暢度、滾動順滑度以及 Tab 點選切換時候界面響應速度。之前版本的魚塘小程式在低端 Android 機(Android vivo Y67)上首屏時間接近6s,在 iPhone 7P 上滾動幀率平均在 34fps - 60fps,點選 Tab 切換瞬時幀率在 30fps 左右,下面針對幾個具體 Case 讨論解決方案。

加載慢

『BEFORE』

為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望

『AFTER』

為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望

問題表現

從打開小程式到最終渲染出來經曆了短暫的白屏

原因分析

加載整體耗時包括小程式容器初始化、資料請求以及請求結果傳回到渲染,需要針對每個時期做優化

優化手段

  • 引入小程式執行個體保活機制,降低小程式啟動耗時
  • 将資料請求提前至 page.onLoad 中,請求階段加入閑魚 Loading 提示,通過互動減少使用者焦慮感
  • 首屏資料離線化,在首屏資料到達前預渲染,在容器測請求提前至與小程式執行個體啟動并行或更前
  • 将首屏資料進行拆分,初始化隻 setData 可見視圖對應的資料

滾動卡頓

為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望
為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望

頁面滾動掉幀感明顯,粘手度低

  • 由于需要監聽頁面滾動距離觸發頂部 Bar 顯示,Page 層監聽了 onScroll 事件,并在回調中頻繁的調用 setData
  • 加載下一頁 Feeds 的請求回調中,解析資料時多次調用 setData
  • Feeds 卡片内部監聽了元件的 onAppear 和 onDisappear 事件,并在回調中調用 setData,目的是為了不重複發送曝光埋點

針對小程式 webview 和 worker 通信的機制,我們需要減少 setData 的調用頻率與傳輸資料大小。

  • 優化了 onScroll 回調邏輯,改為隻有在部分時機(滾動距離在一定範圍内)下才會觸發 setData,且隻做局部渲染
  • 加載下一頁 Feeds 的請求回調優化了資料解析邏輯,隻調用一次 setData,并參考 官方優化建議 使用 $spliceData 渲染長清單
  • 将 setData 的資料大小進行優化,隻傳輸會影響視圖的相關資料
  • 不再監聽元件 onAppear 和 onDisappear 事件,改為監聽元件的 onFirstAppear 事件,隻有第一次 Appear 的時候才執行曝光操作

Tab 切換響應慢

為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望
為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望

加載幾頁 Feeds 後,切換 Tab 開始出現明顯示卡頓,需等待 3-5 秒,部分 Android 機器上更為嚴重,偶爾會 Crash

Tab 切換時在短時間内做了太多事情:切換 Tab Current 狀态、銷毀 Feeds 清單、展示 Loading 動畫、發起資料請求 -> 渲染新清單,這樣高并發大面積的内容更新導緻小程式視圖層資料消費阻塞,進而産生卡頓感。

  • 将 Tab 切換時的任務拆解開,分四個階段進行:
    • 切換 Tab Current 狀态,執行 Tab 切換動畫
    • 在 Tab 切換動畫完成後将頁面滾動到 Tab 剛好 Sticky 住的位置
    • 銷毀 Feeds 清單,展示 Loading 動畫
    • 發起資料請求 -> 渲染
  • 經過這樣的拆解,将之前的『高并發大面積』轉換成了『分階段可控制』的更新方式,随之帶來的就是界面上的流暢

容器層優化

小程式容器通過離線緩存、資料預加載、小程式保活等機制來優化整體性能。然而在富互動場景中,webview 上的控件渲染會遇到很多性能瓶頸,目前阿裡小程式支援在 webview 中内嵌 native 元件來提升整體性能,魚塘小程式中有大量視訊内容場景,使用的 video 元件就是 native 原生元件。這類元件脫離 webview 線程之外渲染,但是由于是覆寫在 webview 之上,是以在 webview 内無論怎樣修改 z-index 都無法将元素覆寫在原生元件之上。

為了解決這個問題,小程式架構同學又設計了 cover-view ,它可以覆寫在 native 元件之上,比如視訊上方的播放按鈕就可以用 cover-view 蓋上去。最終線上魚塘小程式通過同層渲染 video 元件之後使用者側體驗有比較大的提升。

總結與展望

經過優化之後,目前線上魚塘小程式相比之前版本有顯著提升:

為何小程式上線了,他們的内心卻留下遺憾?前言小程式架構業務層優化容器層優化總結與展望

針對這個業務場景下的小程式依然有很多可以繼續優化空間,例如我們将每個魚塘執行個體化獨自小程式,這樣可以針對每個魚塘小程式去進行保活等。此外在小程式性能優化相關上,我們認為可以在研發階段提供一個包含性能告警的容器,通過監聽 setData 的調用頻率與傳輸資料大小,對開發者一些可能會影響性能的代碼寫法進行提示。未來我們會持續在閑魚小程式生态建設上發力,整合集團研發資源建立閑魚小程式研發全鍊路最佳實踐,提供外部服務商入駐開發三方小程式的良好體驗。

繼續閱讀