简体中文
  • react
  • lazy
  • suspense

放心 使用 React.lazy:在快速迭代时安全加载组件的方法

React.lazy 是一种按需加载组件并提升应用性能的绝佳方式。然而,有时它可能会导致一些问题,比如"ChunkLoadError"和"Loading chunk failed"。

Gao
Gao
Founder

困境

如今,在受欢迎的“快速行动并打破常规”的理念下,软件开发正在加速前进。这不是批判——只是事实而已。然而,这种快速节奏有时会导致问题,尤其是在 React 中加载组件时。

如果你正在一个使用 React.lazy 按需加载组件的项目中工作,可能已经遇到过一些问题,比如 ChunkLoadErrorLoading chunk failed。以下是一些可能的原因:

  • 网络问题,例如,用户的网络连接速度慢或不稳定。
  • 用户使用的是应用程序的旧版本,浏览器尝试加载一个已经不存在的 chunk。

通常,简单刷新页面可以解决问题,但这不是一种很好的用户体验。想象一下,当用户导航到另一个路由时,出现一个白屏——这对你的应用来说不是一个很好的形象。

我们能否在追求速度的同时兼顾用户体验的顺畅度呢?当然可以。让我向你展示如何做到(当然是使用 TypeScript)。

解决方案

一种蛮力解决方案是在服务器上保存所有版本的 chunk,这样就不会再有“missing chunk”问题了。随着应用规模的增长,由于磁盘空间需求的增加,这种解决方案可能变得不可行,并且它仍然无法解决网络问题。

考虑到重试或刷新可以解决该问题,我们可以在代码中实现这些解决方案。由于该问题通常发生在用户导航到另一个路由时,我们甚至可以在用户没有注意到的情况下解决它。我们需要做的只是围绕 React.lazy 函数构建一个包装器来处理重试和刷新。

已经有一些关于如何实现这种解决方案的优秀文章,因此我将重点放在解决方案的思想和内部工作原理上。

创建包装器

第一步是创建一个围绕 React.lazy 函数的包装器:

处理重试

对于网络问题,我们可以通过将 importFunction 包裹在 tryImport 函数中来处理重试:

看起来很简单,对吧?你还可以实施 指数退避 算法,以更有效地处理重试。

处理刷新

对于旧版本问题,我们可以通过捕获错误并刷新页面来处理:

然而,这种实现非常危险,因为当错误无法通过刷新解决时,它可能导致无限循环刷新。同时,在刷新期间应用状态将会丢失。因此我们需要 sessionStorage 的帮助来存储我们已经尝试刷新页面的消息:

现在,当我们从 safeLazy 函数捕获到错误时,我们知道它是在刷新时无法解决的问题。

同一页面上的多个懒加载组件

当前实现还有一个隐藏的陷阱。如果在同一页面上有多个懒加载组件,无限刷新循环仍然可能发生,因为其他组件可能会重置 sessionStorage 值。为了解决这个问题,我们可以为每个组件使用一个唯一的 key:

现在,每个组件都有自己唯一的 sessionStorage key,并且可以避免无限刷新循环。我们还可以继续对解决方案进行挑剔,例如:

  • 将所有 key 收集到一个数组中,这样只需要一个存储 key。
  • 设置刷新限制,以允许刷新页面多次而不是仅一次后抛出错误。

但我想你已经明白了这个思路。一个带有测试和配置的全面 TypeScript 解决方案可在 GitHub repository 中找到。我还在 NPM 上发布了 react-safe-lazy 包,你可以立即在你的项目中使用它。

结论

软件开发是一项细致的工作,甚至最小的细节都需要付出努力来解决。我希望这篇文章能帮助你优雅地处理 React.lazy 中的问题,并改善你的应用的用户体验。