0到1:闲鱼高复杂度高性能社区圈子开发实录
簡介: 魚樂圈上線啦~
閑魚會玩社區的重要陣地:會玩圈子今年年初已經上線啦~
作為一款承載著「基于興趣聚集同好人群」的社區型產品,相較于常規導購型產品來說,在業務復雜度、交互復雜度、性能體驗穩定性要求上都要高出許多,像多角色區分、嵌套滾動、多形態 Feeds 無限加載、顏文字等特殊字符處理、頁面直開、視頻播控等場景都是導購場景很少遇到的。
本文將圍繞著會玩圈子的前端設計、開發過程中遇到的典型問題進行介紹。
關鍵設計
如上圖為會玩圈子的設計大圖,可以看到整體的業務邏輯相對較為復雜。并且由于應用中存在多種角色狀態,不同角色的用戶展示界面和操作邏輯也有差異點存在,前端同學進行開發上手的成本較高。
為了降低前端同學在開發過程中對于全局業務理解的成本,減少溝通中信息傳遞容易遺漏的問題。我們在設計之初首先進行了最小模塊的拆分工作。以模塊維度來分配相應的工作,將模塊間存在數據共享和數據通信行為進行梳理拆解,以最優方式來分配數據狀態維護的最小閉環,降低組件間耦合度。
1. 模塊拆分
在此處定義的模塊不是前端日常開發中所講的模塊組件拆分,而是能夠獨立閉環自成體系的一個業務單元。這樣在項目過程中除了部分組件間數據傳遞需要做預先約定外,前端同學僅僅只需要專注于自己所負責的交互場景即可。
??
以圈子模塊主頁為例,根據設計稿我們將它拆分為了三個獨立的業務模塊:圈子信息模塊、信息流模塊和浮層組件模塊,他們無論從功能上還是展示上都完全不同。
- 圈子信息模塊: 偏展示型模塊。交互較少,根據業務數據展示相應的信息模塊內容,需要根據用戶當前的身份展示不同類型的模塊組件,并且可以根據當前用戶的身份來進行權限校驗,在未符合時進行友好Tip提示的能力。
- 信息流模塊: 偏交互型模塊。需要支持多個列表在Tab下嵌套滾動的能力,模塊本身需要維護用戶關注狀態表、黑名單篩選表和視頻播放列表,方便模塊中的子組件進行數據共享。并且列表具備單排流和瀑布流兩種布局模式,列表內元素存在商品、帖子內容兩種類型卡片。支持評論、點贊查看原圖、視頻播放、觸發浮層等多種交互能力。
- 浮層模塊:通用型模塊。允許開發者根據業務需求注冊相應的模塊組件,并且允許配置相應的展示位置、動畫效果和圖層index
2. 狀態值拆分
由于在圈子中有非常多需要共享信息的場景存在,例如用戶相關數據、圈子基礎信息等,僅僅只是基于業務模塊閉環來拆分狀態在此處就不適合了。
因此在完成模塊拆分之后我們對圈子主頁的狀態數據進行了梳理,根據組件最小渲染原則來對狀態進行相應的拆分,如下圖所示:
全局狀態
如上圖,對于有共享訴求的狀態變量,我們優先將這些狀態值匯總到一起以方便統一處理。
但由于是C端場景,交互復雜度不高并且考慮到資源包的大小會對用戶體驗有一定影響。此處的全局狀態的管理方案我們選擇了直接使用Rax原生的useReducer + useContext來進行處理,并將獲取對應實例context 的方法Hooks化以方便開發同學使用,簡單demo代碼如下所示:
業務組件狀態
對于非共享型的數據,則要求放到業務模塊中組件渲染影響最小的容器層來進行維護,以單排流的帖子列表為例:
- 卡片的初始數據通過props形式傳入,單一帖子的交互性數據都保留在帖子元素組件一層來維護。
- 列表容器中除了基礎狀態信息外,僅僅只做視頻播放的控制,不額外觸發容器級的重渲染。
實施調優
在多個業務模塊進行組合調試的過程中,我們發現交互體驗依然有許多不盡人意的問題點:
- 展示模塊過多的情況下,如果在多個Tab下進行數據加載切換過后整個頁面的交互會出現明顯的卡頓感:比如點擊彈出浮層會有明顯的等待時延,翻頁切換Tab時對應的下標移動會不同步。
- 浮層容器中注冊的部分組件由于依賴共享變量,在共享變量變化時也會觸發不必要的重渲染:效果為會跟著閃動一下。
- 網絡狀況不穩定的情況,頁面展示不夠友好的;從用戶點擊路由跳轉到首屏頁面展示的等待時間過于明顯,與我們要求的頁面直開效果相差甚遠。
1. 減少Context.Provider重渲染
使用Context盡管可以提升狀態值傳遞的便捷性,但是伴隨的問題也相當明顯:每一次狀態值更新變化都會觸發整個Context.Provider和下面的子組件重新渲染。
這與我們預期的渲染流程不一致,畢竟我可能只是調整了一個CircleHeader組件所依賴的值,沒必要底下CircleSlider組件及其中的列表組件都需要跟著做調整渲染,這個代價是我們無法接受的。
通過在社區中尋找相應的解決方案,我們發現還是有一定技巧來解決這個問題的:Context.Provider其實組件也保持著Rax組件的一致規則:props.children作為傳入屬性,它如果保持不變就不會觸發值diff,進而也就不會出現重渲染的問題了。
那如何才能做到讓Provider不會由于props.children的變化產生重渲染呢?通過社區提供的資料,我們發現每次執行的都是JSX轉義后的createElement(xxx)。由于每次執行產生的子組件都不一致,所以會導致不必要的重渲染。
為此我們將Context.Provider單獨拆分成為一個專門用于傳狀態值的高階組件,將子組件以props.children的形式傳入:
通過這種方式,我們將CircleApp變為了Stateless Component。只有在首次初始化組件時進行渲染,之后Provider值變化時頁不會重新導致GlobalContextProvider執行createElement(CircleApp``)來重新創建組件實例了,減少不必要的js執行。
2. 調整組件結構
如上圖可以看到在圈子中存在較多彈出浮層組件的場景,在初版設計過程中考慮到浮層組件由于也需要使用到全局共享變量。因此在設計組件結構之初,將浮層容器組件放到了全局共享變量的GlobalContextProvider組件之內。
但在實際體驗過后發現,盡管對于內部的浮層組件獲取共享變量較便利了,但反饋出來的問題也相當明顯:在使用中低端機型時,如果頁面加載的數據過多后會出現明顯的延遲感。并且浮層組件僅僅在真實展示的時候才需要用到相應的狀態值,非展示時其實不需要關系這些數據的具體內容。
?
為此我們調整了浮層容器組件和主頁常駐組件的層級結構,如上圖所示:將常駐組件容器和層容器由原來的嵌套結構優化為了并行結構,兩組件之間的數據通信通過方法調用來觸發。這樣調整之后優點相對比較明顯:首先浮層組件可以更加通用、復用性更強。所有所需參數都是通過傳參的方式傳入,不需要再強依賴全局共享狀態,對于開發同學來說維護起來的成本更低。其次因為減少對共享狀態值的依賴,子組件不必要的重渲染也都得到了優化。對于中低端機型也能提供相對更好的互動體驗。
3. 首屏體驗優化 + 容災機制
在去年下半年的體驗優化升級戰役中閑魚的前端頁面體驗都有了很大的提升:頁面首屏等待時間大幅度降低、內容展示更加友好,各個頻道頁接入漸進式首屏后用戶能更快的查看到內容數據。
但在圈子開發的過程中,我們發現對于個性化推薦的場景之前提出的漸進式首屏方案無法較好的支持。為此我們選擇了降級方案,調整了從圈子廣場頁到圈子主頁及相關子頁面的路由跳轉邏輯。
如上圖,通過制定上下游頁面之間的數據緩存約定來達成容災和提高交互體驗是目的。在每次頁面路由跳轉時都將相應的業務數據進行緩存,在下一級頁面對消費相應的緩存數據。這樣不僅可以在網絡環境較差的情況下提升用戶的體驗。同時在接口報錯時進行可以起到最低程度的有效兜底,避免用戶體感過差。
優化前? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??優化后
在此基礎上為了提升頁面首屏的渲染速度,我們接入提升數據預取方案和離線包緩存方案。將首屏頁面渲染過程中最為耗時的資源包加載過程和首屏接口請求過程做了并行化處理,從而降低了首屏展示的等待時長。
優化前后效果對比:
優化前 優化后
后續展望
會玩圈子的首個版本在遭遇各種小問題后終于順利上線了。在這個過程中解決了部分在之前電商導購場景下未經歷過的問題:例如角色權限管控,多狀態值管理等問題。這些經驗的沉淀對于之后閑魚社區內復雜C端應用體系的成長可以提供一定的助力。
但目前仍有許多的問題點待我們思考優化:
- 目前圈子主頁的首屏平均可交互時長為1000ms左右,用戶從點擊入口到進入主頁內瀏覽基本無需等待。但我們相信通過根據設備類型來做區分,在進入頁面之初降級部分中低端機非必須能力能夠為這一類用戶提供更快的交互體驗。
- 為了突出社區內不同圈子個性點,相信自定義的裝修能力以及定制插件能力在之后是必不可少的。要如何能夠基于現有的架構體系快速接入這些業務訴求,也是我們在現有能力上需要預先思考到的
- 根據業務訴求的變化,如何將從業務模塊中產生的組件盡量做到更通用化并且支持多種容器也是需要解決的。
作者:閑魚技術——龐止
原文鏈接
本文為阿里云原創內容,未經允許不得轉載
?
總結
以上是生活随笔為你收集整理的0到1:闲鱼高复杂度高性能社区圈子开发实录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 33个常见问题!超全Windows排查手
- 下一篇: 如何做好一场技术演讲?