繁體中文(台灣)
  • react
  • lazy
  • suspense

放心使用 React.lazy:在快速迭代中安全加載組件的方法

React.lazy 是一種按需加載組件並提升應用性能的好方法。然而,有時它可能會引發一些問題,例如 "ChunkLoadError" 和 "Loading chunk failed"。

Gao
Gao
Founder

兩難

如今,軟體開發在 "快速行動並打破規則" 的流行哲學下發展迅速。這裡並沒有評判——這只是事情的現狀。然而,這種快速的步伐有時可能會導致問題,特別是在 React 中加載組件時。

如果你正在開發一個使用 React.lazy 按需加載組件的專案,你可能會遇到一些問題,例如 ChunkLoadErrorLoading chunk failed。以下是一些可能的原因:

  • 存在網路問題,例如用戶的網路連接緩慢或不穩定。
  • 用戶使用的是舊版本的應用,瀏覽器試圖加載一個不再存在的 chunk。

通常,簡單刷新頁面可以解決問題,但對用戶來說這不是很好的體驗。想像一下,當用戶導航到另一條路徑時出現白屏的情況 - 對你的應用來說這不太好看。

我們能否在速度需求和順暢用戶體驗之間取得平衡?當然可以。讓我來告訴你如何做到(當然是用 TypeScript)。

解決方案

一個蠻力解決方案是將所有版本的 chunk 保存在伺服器上,這樣就不會出現 "缺少 chunk" 的問題。隨著你的應用增長,這種解決方案可能會因磁碟空間需求增加而變得不可行,同時它仍然無法解決網路問題。

考慮到重試或刷新可以解決問題,我們可以在代碼中實現這些解決方案。由於問題通常發生在用戶導航到另一條路徑時,我們甚至可以在用戶察覺之前解決它。我們需要做的只是建立一個包裹 React.lazy 函數的包裝器,它將處理重試和刷新。

已經有一些很棒的文章討論如何實現這類解決方案,所以我會專注於解決方案的想法和內部運作。

創建包裝器

第一步是為 React.lazy 函數創建一個包裝器:

處理重試

對於網路問題,我們可以通過將 importFunction 包裝在 tryImport 函數中來處理重試:

看起來很簡單,對吧?你還可以實現 指數退避 算法來更高效地處理重試。

處理刷新

對於過時版本問題,我們可以通過捕捉錯誤並刷新頁面來處理刷新:

然而,這種實現非常危險,因為當錯誤無法通過刷新解決時,它可能會導致無限刷新循環。與此同時,應用狀態會在刷新期間丟失。因此,我們需要 sessionStorage 的幫助來存儲我們已經嘗試刷新頁面的消息:

現在,當我們從 safeLazy 函數中捕捉到錯誤時,我們知道這是無法通過刷新解決的問題。

在同一頁面上的多個 lazy 組件

目前的實現仍然藏有隱患。如果你在同一頁面上有多個 lazy 組件,由於其他組件可能會重置 sessionStorage 值,因此無限刷新循環仍然可能發生。為了解決此問題,我們可以為每個組件使用一個唯一鍵:

現在,每個組件將擁有各自的 sessionStorage 鍵,從而避免無限刷新循環。我們可以繼續挑剔這個解決方案,例如:

  • 將所有鍵收集在一個陣列中,這樣只需要一個存儲鍵。
  • 設置刷新限制,以便在拋出錯誤前多次刷新頁面。

但我想你已經掌握了這個想法。在 GitHub 儲存庫 中有一個包括測試和配置的綜合 TypeScript 解決方案。我也在 NPM 上發布了 react-safe-lazy 套件,可以立即用於你的專案中。

結論

軟體開發是一項細緻的工作,即使是最小的細節也可能需要付出努力來解決。希望這篇文章能幫助你優雅地處理 React.lazy 的問題並改善應用的用戶體驗。