將輪播載入的前端記憶體使用量降低 30 倍

2020 年第二季,我們對「體驗」頁面進行了技術重構,改用 React 重新編寫並整合 REST API。但在實作過程中,我們發現了一個有趣的問題。
體驗頁面上有約 20 個輪播區,每個輪播區至少包含 60 個方塊。因此,該頁面將渲染超過 1,200 項體驗內容,包含標題、用戶評分、同時在線人數及縮圖等相關資訊。在每個輪播區中,點擊右側滾動條會發送請求,若還有更多內容則會載入下一批體驗項目。 隨後,請求所獲取的新資料將追加至輪播區中,這意味著 DOM 中會新增更多節點,且瀏覽器需渲染更多縮圖。隨著使用者滑動以請求更多資料,瀏覽器內的記憶體使用量將持續增加,進而導致使用者體驗變慢並開始出現延遲。

記憶體分析
我們可以使用 Chrome 開發者工具來分析此問題。第一個快照是在頁面初始載入時擷取的,第二個快照則是在使用者點擊輪播圖中的「下一頁」按鈕 12 次後擷取的。我們觀察到,由於超過 100 個字串節點以及縮圖渲染量的增加,記憶體使用量幾乎增加了 9.7MB。

解決方案
當輪播圖捲動時,使用者僅能看到輪播視窗內的項目。例如,在 1920×1080 解析度的筆記型電腦上,於全螢幕瀏覽器模式下,每次僅會顯示約 9 個項目。因此,沒有必要將所有不可見的節點和縮圖一併渲染,也不必將請求返回的更多資料載入 DOM 中。
因此,我們的構想是:在首次從 API 請求取得原始資料後,我們會建立一個與渲染長度相同的陣列,以準備渲染至 DOM。在渲染陣列中,我們只需填入足以填滿當前顯示區域並可捲動至下一項的資料。陣列的其餘部分,僅會在顯示視窗捲動至其附近時才進行填充。 讓我們定義兩個索引來記錄渲染資料的範圍:renderingStartIndex 和 renderingEndIndex。我們建立一個游標,用以標示清單中哪些起始位置是可見的。在捲動時,我們需要持續將游標更新至第一個可見項目的索引位置。在捲動動畫結束之前,我們需要檢查下一個游標是否位於渲染資料陣列的範圍內。 根據輪播視窗大小與卡片尺寸,我們可以輕鬆判斷顯示視窗內能容納多少個可見項目。接著,我們需要確保當前視窗與下一個視窗中的項目都在渲染資料陣列的範圍內。若不在範圍內,我們會從原始資料中擷取項目來填補空缺,同時清除游標位置之前的所有資料。此機制適用於使用者向右或向左捲動的情況。 當使用者向右捲動時,系統會在背景載入更多資料,並在每次收到新資料後,將原始資料填入顯示區。不過,原始資料不會參與頁面渲染。


記憶體分析
讓我們再次檢視 Chrome 開發者工具。與先前相同,第一個快照是在初始化時擷取的,而第二個快照則是在向右捲動 12 次後擷取的。現在,載入 100 多個項目僅需增加 0.3MB 的記憶體,相較於之前的 9.7MB。

接下來要做什麼?
除了改進輪播元件以處理大型資料視窗外,當我們重構功能或頁面時,還需增加更多基準測試來擷取效能資訊。
儘管現代網路應用堆疊已逐漸向前端轉移,但捕捉並優化前端遙測資料的重要性,與傳統後端遙測同樣關鍵。
江穎是 Roblox 的首席前端軟體工程師。身為 Roblox 的首位前端工程師,她致力於將現代前端技術堆疊引入 Roblox,並優化開發與部署流程。她亦曾參與開發即時功能,例如聊天、通知串流及語音功能。
Roblox 公司及本部落格均不對任何公司或服務予以背書或支持。此外,對於本部落格所含資訊的準確性、可靠性或完整性,亦不作任何保證或承諾。


